openclaw - ✅(Solved) Fix [plugin sdk] Consolidate author surface, lifecycle semantics, and export sprawl [5 pull requests, 2 comments, 2 participants]
ON THIS PAGE
Recommended Tools
×6Utilities matched from this issue’s tags and category — try them while you read without losing context.
GitHub issue graph ai analysis
Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.
The report is written in English Markdown for sharing and archival.
Helpful · Quick feedback
Root Cause
This is the highest-value first step because it reduces future API sprawl without breaking any current features.
Fix Action
Fixed
- Fixed by PR: [plugin sdk] Phase 1: consolidate host workflow seams and media extraction runtime (https://github.com/openclaw/openclaw/pull/80229)
- Fixed by PR: [plugin sdk] Add structured extraction media runtime (https://github.com/openclaw/openclaw/pull/79334)
- Fixed by PR: [plugin sdk] Add session action gateway protocol (https://github.com/openclaw/openclaw/pull/75578)
- Fixed by PR: [plugin sdk] Add host-mediated session attachments (https://github.com/openclaw/openclaw/pull/75581)
- Fixed by PR: [plugin sdk] Add scheduled session turns (https://github.com/openclaw/openclaw/pull/75588)
PR fix notes
PR #80229: [plugin sdk] Phase 1: consolidate host workflow seams and media extraction runtime
- Repository: openclaw/openclaw
- Author: 100yenadmin
- State: closed | merged: False
- Link: https://github.com/openclaw/openclaw/pull/80229
Description (problem / solution / changelog)
Closes #80219.
This is Phase 1 of the whole-surface Plugin SDK consolidation plan in #80219. It intentionally cleans up the host-workflow/session-control slice plus the active proof-gated seams, not the entire SDK/API surface in one PR.
This PR consolidates the active Plugin SDK host-hook/runtime work into one cleanup-first branch instead of asking reviewers to reason across four separate PRs plus a follow-up architecture issue.
It folds together:
- #75578: typed session action registration/dispatch plus scope enforcement
- #75581: host-mediated session attachments
- #75588: scheduled session turns
- #79334: structured extraction media runtime
It also adds one narrow consolidation slice on top:
- additive grouped host-hook aliases on
OpenClawPluginApi - shared late-callability metadata for runtime-callable post-register APIs
Why this matters
OpenClaw is moving more host behavior into plugins and extensions, but the current Plugin SDK surface was heading toward a flat pile of new top-level verbs. That makes every new capability feel like permanent API expansion even when the underlying runtime seams are valid.
This PR keeps the feature work, but makes the shape easier to grow from:
- typed session actions stay a clean control/RPC seam
- session attachments and scheduled turns stay host-mediated workflow seams
- structured extraction stays inside media understanding instead of widening generic
runtime.llm - the new grouped aliases make the host-hook-heavy parts of the SDK read like families instead of unrelated top-level escapes
What this PR does
1. Integrates the active host-hook/runtime slices
- session action gateway protocol, dispatch, and scope enforcement
- host-mediated session attachment delivery
- scheduled session turns plus tag-based unscheduling
- structured extraction in the media-understanding runtime
2. Adds a narrow consolidation layer
api.session.state.*api.session.workflow.*api.session.controls.*api.agent.events.*api.runContext.*api.lifecycle.*
These are additive aliases over the current flat methods. Existing plugin code keeps working.
3. Centralizes loader late-callability policy
Instead of loader.ts hardcoding post-register callable methods inline, this PR moves that policy into shared metadata so the runtime-callable host-hook seams have one source of truth.
Current runtime-callable post-register methods remain:
sendSessionAttachmentscheduleSessionTurnunscheduleSessionTurnsByTag
Architecture
flowchart TD
A["Plugin author surface"] --> B["OpenClawPluginApi"]
B --> C["Flat legacy methods remain supported"]
B --> D["Additive grouped aliases"]
D --> E["session.state"]
D --> F["session.workflow"]
D --> G["session.controls"]
D --> H["agent.events"]
D --> I["runContext"]
D --> J["lifecycle"]
F --> K["next-turn injection"]
F --> L["attachments"]
F --> M["scheduled turns"]
G --> N["session actions"]
H --> O["event subscribe/emit"]
J --> P["runtime cleanup"]
Q["mediaUnderstanding runtime"] --> R["structured extraction"]
Q --> S["explicit provider/model media inference lane"]Plugin families this supports
This combined surface is meant to support more than one product feature. Concretely, it is a foundation for:
- approval and operator-control plugins
- reminder, follow-up, and scheduled workflow plugins
- channel companion plugins that need outbound attachment delivery
- session-aware UI/control plugins
- background monitor and lifecycle plugins
- media-understanding and structured extraction plugins
Boundaries
- This does not collapse structured extraction into generic
runtime.llm.complete. - This does not remove the flat Plugin SDK methods yet.
- This does not try to genericize all workflow seams into one catch-all runtime mutation API.
- This keeps the upstream ACP streaming/default-delivery behavior intact.
Real behavior proof
Behavior or issue addressed:
This combined branch keeps the new host-hook seams narrow while proving they work together on the #80229 head. After this patch, typed plugin session actions dispatch through the Gateway with the action-declared operator scope bundle, grouped late-call workflow aliases remain callable after register() closes for bundled-plugin attachment and scheduled-turn operations, registration-only APIs stay blocked after register, and bounded structured extraction stays inside the media-understanding runtime instead of widening generic runtime.llm.
Real environment tested:
Local macOS checkout at /Volumes/LEXAR/repos/openclaw-plugin-sdk-consolidation on head 564400622821b7f89b374f2e7c25ac0de61bfa4d. I used an isolated temp workspace/state/config root at /var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs, started a token-auth loopback Gateway on ws://127.0.0.1:18997, ran live gateway call requests for the session-action seam, ran a standalone bundled-origin guarded API script against that live cron.* backend for grouped workflow aliases, and ran a standalone runtime proof script for extractStructuredWithModel(...).
Exact steps or command run after this patch:
- Started an isolated token-auth Gateway from the merged head:
HOME=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/home \ OPENCLAW_WORKSPACE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/workspace \ OPENCLAW_STATE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/state \ OPENCLAW_CONFIG_PATH=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/config/openclaw.json \ pnpm openclaw gateway run --allow-unconfigured --auth token --token proof-token --bind loopback --port 18997 - Called the typed session-action seam on that Gateway once with valid payload and once with invalid payload:
HOME=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/home \ OPENCLAW_WORKSPACE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/workspace \ OPENCLAW_STATE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/state \ OPENCLAW_CONFIG_PATH=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/config/openclaw.json \ pnpm openclaw gateway call plugins.sessionAction --json --token proof-token --url ws://127.0.0.1:18997 --params '{"pluginId":"proof-combined","actionId":"approve","sessionKey":"agent:main:main","payload":{"message":"post-fix"}}' HOME=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/home \ OPENCLAW_WORKSPACE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/workspace \ OPENCLAW_STATE_DIR=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/state \ OPENCLAW_CONFIG_PATH=/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/config/openclaw.json \ pnpm openclaw gateway call plugins.sessionAction --json --token proof-token --url ws://127.0.0.1:18997 --params '{"pluginId":"proof-combined","actionId":"approve","sessionKey":"agent:main:main","payload":{"message":42}}' - Ran a standalone
node --import tsxproof script from repo root that:- creates a real bundled-origin plugin record through
createPluginRegistry(...) - captures the guarded API via
loader.__testing.runPluginRegisterSync(...) - invokes
capturedApi.session.workflow.sendSessionAttachment(...) - verifies
capturedApi.session.state.registerSessionExtension(...)stays blocked afterregister() - invokes
capturedApi.session.workflow.scheduleSessionTurn(...)twice plus invalid tag/deleteAfterRun cases - calls
capturedApi.session.workflow.unscheduleSessionTurnsByTag(...) - queries live
cron.listbefore and after cleanup through the running Gateway
- creates a real bundled-origin plugin record through
- Ran a standalone
node --import tsxruntime proof script from repo root that invokesruntime.mediaUnderstanding.extractStructuredWithModel(...)once with image+text input and once with text-only input.
Evidence after fix:
$ pnpm openclaw gateway call plugins.sessionAction --json --token proof-token --url ws://127.0.0.1:18997 --params '{"pluginId":"proof-combined","actionId":"approve","sessionKey":"agent:main:main","payload":{"message":"post-fix"}}'
{
"ok": true,
"result": {
"approved": true,
"message": "post-fix",
"sessionKey": "agent:main:main",
"scopes": [
"operator.admin",
"operator.read",
"operator.write",
"operator.approvals",
"operator.pairing",
"operator.talk.secrets"
]
},
"continueAgent": true
}
$ pnpm openclaw gateway call plugins.sessionAction --json --token proof-token --url ws://127.0.0.1:18997 --params '{"pluginId":"proof-combined","actionId":"approve","sessionKey":"agent:main:main","payload":{"message":42}}'
Gateway call failed: GatewayClientRequestError: plugin session action payload does not match schema: message: must be string
$ node --import tsx <bundled-origin guarded workflow proof>
{
"attachment": {
"ok": true,
"channel": "proofchat",
"deliveredTo": "12345",
"count": 1
},
"deliveries": [
{
"to": "12345",
"text": "attachment ready",
"mediaUrl": "/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr80229-proof-qGy5Vs/workspace/proof-report.txt",
"accountId": "default"
}
],
"sessionExtensionsBefore": 0,
"sessionExtensionsAfter": 0,
"first": {
"id": "f2f6d165-2356-402c-ab9a-9b67b4e62f31",
"pluginId": "proof-combined",
"sessionKey": "agent:main:main",
"kind": "session-turn"
},
"second": {
"id": "32447765-6139-4238-bc4e-f3fb15ed333d",
"pluginId": "proof-combined",
"sessionKey": "agent:main:main",
"kind": "session-turn"
},
"badTag": null,
"badDelete": null,
"cronJobsAfterSchedule": {
"jobs": [
{
"id": "32447765-6139-4238-bc4e-f3fb15ed333d",
"name": "plugin:proof-combined:tag:nudge:agent:main:main:2396fa1c-f78a-49e6-93d0-9e0013a02afa",
"deleteAfterRun": true,
"sessionTarget": "session:agent:main:main",
"payload": { "kind": "agentTurn", "message": "wake two" },
"delivery": { "mode": "none" }
},
{
"id": "f2f6d165-2356-402c-ab9a-9b67b4e62f31",
"name": "plugin:proof-combined:tag:nudge:agent:main:main:4e148d8e-5e2b-4cdc-bc80-ce50f6cd8f47",
"deleteAfterRun": true,
"sessionTarget": "session:agent:main:main",
"payload": { "kind": "agentTurn", "message": "wake one" },
"delivery": { "mode": "announce", "channel": "last" }
}
],
"total": 2
},
"removed": {
"removed": 2,
"failed": 0
},
"cronJobsAfterCleanup": {
"jobs": [],
"total": 0
}
}
$ node --import tsx <structured extraction runtime proof>
{
"success": {
"text": "{\"summary\":\"red square\",\"tags\":[\"shape\"]}",
"model": "gpt-5.4",
"provider": "codex",
"contentType": "json",
"parsed": {
"summary": "red square",
"tags": ["shape"]
}
},
"authProfileIds": ["openai-codex:work"],
"requestMethods": ["model/list", "thread/start", "turn/start"],
"threadStart": {
"approvalPolicy": "on-request",
"sandbox": "read-only",
"serviceName": "OpenClaw",
"dynamicTools": [],
"ephemeral": true
},
"guardError": "Structured extraction requires at least one image input."
}Observed result after fix:
On the combined #80229 head, plugins.sessionAction dispatch succeeds with typed output and reaches the handler under the action-declared operator scope bundle, while malformed payloads are rejected before handler execution. The grouped late-call workflow aliases on api.session.workflow.* remain callable after register() closes for a bundled plugin, the registration-only session.state.registerSessionExtension(...) path stays blocked (sessionExtensionsBefore/After remained 0), a relative attachment path resolves against the session workspace and is delivered to the active direct-outbound route, two real host cron jobs are created and visible through cron.list, invalid tag/deleteAfterRun inputs fail closed, and tagged cleanup removes both jobs. Structured extraction stays bounded in the media-understanding runtime, forwards the selected auth profile into the provider-owned runtime, returns parsed JSON for image input, and rejects text-only calls before provider dispatch.
What was not tested:
This proof did not run a full end-to-end Control UI or native Apple client flow, and it did not use a credentialed live Codex desktop session. The live evidence here is intentionally limited to merged-head Gateway dispatch, bundled-origin guarded workflow aliases against a real cron.* backend, direct-outbound attachment delivery, and bounded runtime/provider structured extraction behavior.
Verification
Local verification on this combined branch:
node scripts/run-vitest.mjs run --config test/vitest/vitest.config.ts src/plugins/loader.test.ts --maxWorkers=1
node scripts/run-vitest.mjs run --config test/vitest/vitest.contracts-plugin.config.ts src/plugins/contracts/run-context-lifecycle.contract.test.ts src/plugins/contracts/session-actions.contract.test.ts --maxWorkers=1
pnpm plugin-sdk:api:gen
node scripts/run-oxlint-shards.mjs --threads=8Notes for reviewers
- The feature work here originated as four active PR lines because each seam was independently useful.
- The maintainer concern about API accretion is valid, so this branch intentionally adds only the smallest consolidation layer that improves the author-facing shape without re-plumbing the runtime.
- If this direction lands, the next cleanup step should be export-surface reduction and alias/deprecation of the obvious debt lanes described in #80219.
Changed files
CHANGELOG.md(modified, +5/-0)apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift(modified, +208/-0)docs/.generated/plugin-sdk-api-baseline.sha256(modified, +2/-2)docs/plugins/architecture-internals.md(modified, +28/-0)docs/plugins/sdk-overview.md(modified, +18/-0)docs/plugins/sdk-runtime.md(modified, +27/-0)docs/plugins/sdk-subpaths.md(modified, +1/-1)extensions/codex/media-understanding-provider.test.ts(modified, +160/-2)extensions/codex/media-understanding-provider.ts(modified, +198/-10)extensions/googlechat/config-api.ts(added, +2/-0)extensions/googlechat/src/config-schema.ts(modified, +1/-1)extensions/matrix/src/matrix/monitor/handler.test-helpers.ts(modified, +2/-1)extensions/matrix/src/matrix/monitor/handler.test.ts(modified, +9/-0)extensions/telegram/api.test.ts(added, +16/-0)extensions/telegram/api.ts(modified, +8/-0)extensions/telegram/src/channel.gateway.test.ts(modified, +47/-0)extensions/telegram/src/format.ts(modified, +6/-2)extensions/telegram/src/outbound-adapter.test.ts(modified, +5/-6)extensions/telegram/src/outbound-adapter.ts(modified, +12/-2)scripts/protocol-gen-swift.ts(modified, +246/-1)src/agents/tools/gateway.test.ts(modified, +66/-0)src/agents/tools/gateway.ts(modified, +1/-1)src/config/sessions/delivery-info.test.ts(modified, +223/-3)src/config/sessions/delivery-info.ts(modified, +154/-24)src/gateway/call.test.ts(modified, +24/-0)src/gateway/call.ts(modified, +2/-2)src/gateway/method-scopes.test.ts(modified, +94/-0)src/gateway/method-scopes.ts(modified, +55/-1)src/gateway/protocol/index.ts(modified, +11/-0)src/gateway/protocol/schema/agent.ts(modified, +6/-0)src/gateway/protocol/schema/plugins.ts(modified, +35/-0)src/gateway/protocol/schema/protocol-schemas.ts(modified, +8/-0)src/gateway/protocol/schema/types.ts(modified, +2/-0)src/gateway/server-methods-list.ts(modified, +1/-0)src/gateway/server-methods/plugin-host-hooks.ts(modified, +314/-0)src/gateway/server-methods/send.test.ts(modified, +22/-0)src/gateway/server-methods/send.ts(modified, +6/-0)src/infra/outbound/formatting.ts(modified, +1/-0)src/infra/outbound/message.channels.test.ts(modified, +17/-0)src/infra/outbound/message.ts(modified, +6/-0)src/media-understanding/runtime-types.ts(modified, +25/-0)src/media-understanding/runtime.test.ts(modified, +155/-0)src/media-understanding/runtime.ts(modified, +40/-1)src/media-understanding/types.ts(modified, +41/-0)src/plugin-sdk/core.ts(modified, +10/-0)src/plugin-sdk/media-understanding-runtime.ts(modified, +2/-0)src/plugin-sdk/media-understanding.ts(modified, +5/-0)src/plugin-sdk/plugin-entry.ts(modified, +20/-0)src/plugin-sdk/plugin-test-api.ts(modified, +8/-2)src/plugin-sdk/test-helpers/plugin-runtime-mock.ts(modified, +2/-0)src/plugins/agent-event-emission.ts(added, +84/-0)src/plugins/api-builder.ts(modified, +26/-2)src/plugins/api-facades.ts(added, +36/-0)src/plugins/api-lifecycle.ts(added, +36/-0)src/plugins/captured-registration.test.ts(modified, +47/-1)src/plugins/captured-registration.ts(modified, +27/-5)src/plugins/contracts/bundled-extension-config-api-guardrails.test.ts(modified, +1/-1)src/plugins/contracts/host-hooks.contract.test.ts(modified, +57/-0)src/plugins/contracts/run-context-lifecycle.contract.test.ts(modified, +6/-6)src/plugins/contracts/scheduled-turns.contract.test.ts(added, +1216/-0)src/plugins/contracts/session-actions.contract.test.ts(added, +1018/-0)src/plugins/contracts/session-attachments.contract.test.ts(added, +877/-0)src/plugins/host-hook-cleanup.ts(modified, +3/-0)src/plugins/host-hook-runtime.ts(modified, +18/-2)src/plugins/host-hook-workflow.ts(added, +781/-0)src/plugins/host-hooks.ts(modified, +127/-0)src/plugins/loader.test.ts(modified, +72/-0)src/plugins/loader.ts(modified, +18/-3)src/plugins/registry-empty.ts(modified, +1/-0)src/plugins/registry-lifecycle.ts(modified, +6/-0)src/plugins/registry-types.ts(modified, +10/-0)src/plugins/registry.ts(modified, +215/-2)src/plugins/runtime-state.ts(modified, +1/-0)src/plugins/runtime.channel-pin.test.ts(modified, +110/-0)src/plugins/runtime.test.ts(modified, +17/-0)src/plugins/runtime.ts(modified, +86/-24)src/plugins/runtime/index.test.ts(modified, +1/-0)src/plugins/runtime/index.ts(modified, +3/-0)src/plugins/runtime/types-core.ts(modified, +1/-0)src/plugins/schema-validator.ts(modified, +6/-4)src/plugins/types.ts(modified, +112/-0)
PR #79334: [plugin sdk] Add structured extraction media runtime
- Repository: openclaw/openclaw
- Author: 100yenadmin
- State: open | merged: False
- Link: https://github.com/openclaw/openclaw/pull/79334
Description (problem / solution / changelog)
Why this matters
OpenClaw plugins increasingly need to turn unstructured user content into safe, typed data: receipts into expense records, screenshots into support evidence, invoices into accounting fields, customer messages into CRM notes, PDFs into knowledge-base snippets, and product photos into searchable inventory metadata.
Today each plugin has to choose between two bad options:
- implement its own model/auth/runtime bridge, usually requiring another user-managed API key; or
- add product-specific extraction routes to core, which does not scale as the plugin ecosystem grows.
This PR adds the missing middle layer: a generic structured extraction capability in the media-understanding SDK. Product plugins keep owning their routes, schemas, storage, and UX, while OpenClaw owns the provider/runtime boundary, auth source, safety posture, and typed SDK contract.
What plugin authors can build with this
Examples this unlocks without adding plugin-specific logic to OpenClaw core:
- Support plugins: extract error messages, stack traces, product names, issue category, severity, and reproduction steps from screenshots.
- Knowledge-base plugins: convert documents or screenshots into normalized metadata and searchable evidence records.
- CRM/sales plugins: extract companies, people, dates, action items, sentiment, and deal updates from inbound media plus short text context.
- Finance/admin plugins: extract vendor, total, currency, tax, due date, and line-item hints from receipts or invoices.
- Inventory/media plugins: extract labels, visible text, tags, object categories, and image summaries from uploaded photos.
- Migration/import plugins: map arbitrary image inputs into a plugin-owned JSON schema before writing to the plugin's own database.
The important part: the plugin defines the schema and decides what to do with the result. OpenClaw only provides the generic, bounded extraction lane.
New SDK shape
This PR adds:
- optional provider method:
MediaUnderstandingProvider.extractStructured(...) - runtime helper:
api.runtime.mediaUnderstanding.extractStructuredWithModel(...) - typed inputs for images plus optional supplemental text context
- optional
schemaName,jsonSchema,jsonMode, andtimeoutMs - controlled result metadata: raw
text, parsed JSON when JSON mode is enabled, model/provider, and content type
Example plugin call:
const result = await api.runtime.mediaUnderstanding.extractStructuredWithModel({
provider: "codex",
model: "gpt-5.5",
input: [
{
type: "image",
buffer: receiptImageBuffer,
fileName: "receipt.png",
mime: "image/png",
},
{ type: "text", text: "Prefer the printed total over handwritten notes." },
],
instructions: "Extract vendor, total, and searchable tags.",
schemaName: "receipt.evidence",
jsonSchema: {
type: "object",
properties: {
vendor: { type: "string" },
total: { type: "number" },
tags: { type: "array", items: { type: "string" } },
},
required: ["vendor", "total"],
},
cfg: api.config,
});Runtime architecture
flowchart LR
Plugin["Plugin route, skill, or importer"] --> Runtime["api.runtime.mediaUnderstanding.extractStructuredWithModel"]
Runtime --> Provider["MediaUnderstandingProvider.extractStructured"]
Provider --> HostRuntime["Provider-owned host runtime"]
HostRuntime --> Result["JSON result or controlled error"]
Result --> Plugin
Plugin --> Storage["Plugin-owned storage, tools, or user workflow"]For the bundled Codex provider, this uses the existing Codex app-server/OAuth path rather than requiring a user-supplied model API key.
flowchart LR
Plugin["Any OpenClaw plugin"] --> SDK["Structured extraction SDK"]
SDK --> CodexProvider["Codex media-understanding provider"]
CodexProvider --> AppServer["Codex app-server / OAuth runtime"]
AppServer --> BoundedTurn["Ephemeral no-tools turn"]
BoundedTurn --> JSON["Parsed JSON or controlled error"]Safety and boundaries
The Codex implementation keeps the same bounded posture as image understanding:
- ephemeral thread
- read-only sandbox
- no dynamic tools
- approval policy set to
on-request, with approval requests denied by the provider handler - timeout enforcement
- model modality validation before turn start
- JSON parsing failure returned as a controlled error
- text-only extraction rejected at the runtime seam, keeping this image-first instead of turning it into a generic text completion lane
- no product-specific route names, storage models, or schemas in OpenClaw core
This is intentionally a platform seam, not a feature-specific integration.
What changed
- Adds structured extraction request/result types to the media-understanding SDK.
- Adds
extractStructuredWithModel(...)to the plugin runtime media-understanding facade. - Implements
extractStructured(...)in the bundled Codex provider. - Preserves explicit config-provider image descriptions by keeping
describeImageFileWithModel(...)on the full media-understanding registry instead of narrowing it to manifest-only plugin providers. - Forwards structured extraction auth-profile selection through the runtime helper so provider-owned OAuth/app-server runtimes can honor plugin-selected credentials.
- Narrows the new seam to image-first extraction with optional supplemental text context instead of overlapping general text-only completion surfaces.
- Adds tests for bounded Codex structured extraction, invalid JSON/schema handling, runtime routing, auth-profile forwarding, image-required guardrails, direct image-model registry routing, provider lookup failure, and runtime API exposure.
- Documents the new runtime helper and the plugin/core ownership boundary.
- Adds the required changelog entry for the new plugin SDK/runtime capability.
Relationship to existing LLM surfaces
OpenClaw already has api.runtime.llm.complete for trusted plugin text completions, and llm-task for workflow/tool-level JSON tasks. This PR is narrower and lower-level: a provider SDK/runtime media-understanding seam for schema-shaped extraction over image inputs with optional text context. That keeps extraction provider-owned and plugin-consumable without turning it into a general-purpose arbitrary Codex call API.
Non-goals
- This does not add a product-specific extraction route to OpenClaw core.
- This does not choose any plugin's storage model or JSON schema.
- This does not replace existing image/audio/video media-understanding helpers.
- This does not require plugins to use Codex; other providers can implement the same optional method.
- This does not expand into generic text-only extraction; callers that want arbitrary text completions should keep using the existing LLM surfaces.
Background
This closes openclaw/openclaw#79321.
The immediate downstream need came from a GBrain/OpenClaw integration, but the implementation here is deliberately generic. GBrain, support, CRM, finance, inventory, migration, and knowledge-base plugins can all consume the same SDK seam while keeping their own product-specific routes and schemas outside OpenClaw core.
Real behavior proof
Behavior or issue addressed: The rebased branch exposes a typed plugin-runtime structured extraction seam that dispatches through a registered media-understanding provider, preserves the bounded Codex worker defaults, forwards the selected auth profile into the provider-owned runtime, and rejects text-only calls before provider dispatch.
Real environment tested: Local macOS OpenClaw checkout at /Users/lume/openclaw-review-worktrees/pr-79334-rebase, rebased head 78cfe4a76161fc7d3029beb4edcf7120a94a4d8b, using a standalone node --import tsx proof command outside Vitest. The proof registers the real bundled Codex media-understanding provider in the active plugin runtime registry with a stubbed app-server client, then calls createPluginRuntime().mediaUnderstanding.extractStructuredWithModel(...) once with image-plus-text input and once with text-only input.
Exact steps or command run after this patch:
cd /Users/lume/openclaw-review-worktrees/pr-79334-rebase
node --import tsx <<'EOF'
import { buildCodexMediaUnderstandingProvider } from './extensions/codex/media-understanding-provider.ts';
import { createPluginRuntime } from './src/plugins/runtime/index.ts';
import { createEmptyPluginRegistry } from './src/plugins/registry-empty.ts';
import { resetPluginRuntimeStateForTest, setActivePluginRegistry } from './src/plugins/runtime.ts';
function codexModel(inputModalities = ['text', 'image']) {
return {
id: 'gpt-5.4',
model: 'gpt-5.4',
upgrade: null,
upgradeInfo: null,
availabilityNux: null,
displayName: 'gpt-5.4',
description: 'GPT-5.4',
hidden: false,
supportedReasoningEfforts: [{ reasoningEffort: 'low', description: 'fast' }],
defaultReasoningEffort: 'low',
inputModalities,
supportsPersonality: false,
additionalSpeedTiers: [],
isDefault: true,
};
}
function threadStartResult() {
return {
thread: {
id: 'thread-1',
sessionId: 'session-1',
forkedFromId: null,
preview: '',
ephemeral: true,
modelProvider: 'openai',
createdAt: 1,
updatedAt: 1,
status: { type: 'idle' },
path: null,
cwd: process.cwd(),
cliVersion: '0.125.0',
source: 'unknown',
agentNickname: null,
agentRole: null,
gitInfo: null,
name: null,
turns: [],
},
model: 'gpt-5.4',
modelProvider: 'openai',
serviceTier: null,
cwd: process.cwd(),
instructionSources: [],
approvalPolicy: 'on-request',
approvalsReviewer: 'user',
sandbox: { type: 'dangerFullAccess' },
permissionProfile: null,
reasoningEffort: null,
};
}
function turnStartResult(status = 'inProgress', items = []) {
return {
turn: {
id: 'turn-1',
status,
items,
error: null,
startedAt: null,
completedAt: null,
durationMs: null,
},
};
}
function createFakeClient(responseText) {
const notifications = new Set();
const requestHandlers = new Set();
const requests = [];
const request = async (method, params) => {
requests.push({ method, params });
if (method === 'model/list') return { data: [codexModel()], nextCursor: null };
if (method === 'thread/start') return threadStartResult();
if (method === 'turn/start') {
for (const notify of notifications) {
notify({ method: 'item/agentMessage/delta', params: { threadId: 'thread-1', turnId: 'turn-1', itemId: 'msg-1', delta: responseText } });
notify({ method: 'turn/completed', params: { threadId: 'thread-1', turnId: 'turn-1', turn: turnStartResult('completed').turn } });
}
for (const handler of requestHandlers) handler({ method: 'item/permissions/requestApproval' });
return turnStartResult();
}
return {};
};
return {
client: {
request,
addNotificationHandler(handler) { notifications.add(handler); return () => notifications.delete(handler); },
addRequestHandler(handler) { requestHandlers.add(handler); return () => requestHandlers.delete(handler); },
close() {},
},
requests,
};
}
const authProfileIds = [];
const { client, requests } = createFakeClient('{"summary":"red square","tags":["shape"]}');
const provider = buildCodexMediaUnderstandingProvider({
clientFactory: async (_startOptions, authProfileId) => {
authProfileIds.push(authProfileId ?? null);
return client;
},
});
const registry = createEmptyPluginRegistry();
registry.mediaUnderstandingProviders.push({
pluginId: 'codex',
pluginName: 'Codex',
source: 'proof-script',
provider,
});
setActivePluginRegistry(registry, 'proof-script', 'default', process.cwd());
const runtime = createPluginRuntime();
const success = await runtime.mediaUnderstanding.extractStructuredWithModel({
provider: 'codex',
model: 'gpt-5.4',
input: [
{
type: 'image',
buffer: Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+kX3sAAAAASUVORK5CYII=', 'base64'),
fileName: 'red-square.png',
mime: 'image/png',
},
{ type: 'text', text: 'Return searchable evidence for the uploaded image.' },
],
instructions: 'Return JSON with summary and tags.',
schemaName: 'proof.red-square',
jsonSchema: {
type: 'object',
properties: {
summary: { type: 'string' },
tags: { type: 'array', items: { type: 'string' } },
},
required: ['summary'],
},
profile: 'openai-codex:work',
cfg: {},
agentDir: process.cwd(),
});
let guardError = null;
try {
await runtime.mediaUnderstanding.extractStructuredWithModel({
provider: 'codex',
model: 'gpt-5.4',
input: [{ type: 'text', text: 'No image present.' }],
instructions: 'Return JSON.',
cfg: {},
agentDir: process.cwd(),
});
} catch (error) {
guardError = error instanceof Error ? error.message : String(error);
}
console.log(JSON.stringify({
success,
authProfileIds,
requestMethods: requests.map((entry) => entry.method),
threadStart: requests.find((entry) => entry.method === 'thread/start')?.params,
turnInput: requests.find((entry) => entry.method === 'turn/start')?.params?.input,
guardError,
}, null, 2));
resetPluginRuntimeStateForTest();
EOFEvidence after fix:
{
"success": {
"text": "{\"summary\":\"red square\",\"tags\":[\"shape\"]}",
"model": "gpt-5.4",
"provider": "codex",
"contentType": "json",
"parsed": {
"summary": "red square",
"tags": [
"shape"
]
}
},
"authProfileIds": [
"openai-codex:work"
],
"requestMethods": [
"model/list",
"thread/start",
"turn/start"
],
"threadStart": {
"model": "gpt-5.4",
"modelProvider": "openai",
"cwd": "/Users/lume/openclaw-review-worktrees/pr-79334-rebase",
"approvalPolicy": "on-request",
"sandbox": "read-only",
"serviceName": "OpenClaw",
"developerInstructions": "You are OpenClaw's bounded structured-extraction worker. Return only the requested extraction. Do not call tools, edit files, ask follow-up questions, or include secrets.",
"dynamicTools": [],
"experimentalRawEvents": true,
"persistExtendedHistory": false,
"ephemeral": true
},
"turnInput": [
{
"type": "text",
"text": "Return JSON with summary and tags.\n\nSchema name: proof.red-square\n\nJSON schema:\n{\"type\":\"object\",\"properties\":{\"summary\":{\"type\":\"string\"},\"tags\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}},\"required\":[\"summary\"]}\n\nReturn valid JSON only. Do not wrap the JSON in Markdown fences.",
"text_elements": []
},
{
"type": "image",
"url": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+kX3sAAAAASUVORK5CYII="
},
{
"type": "text",
"text": "Return searchable evidence for the uploaded image.",
"text_elements": []
}
],
"guardError": "Structured extraction requires at least one image input."
}Observed result after fix: The plugin runtime facade dispatched extractStructuredWithModel(...) through the registered Codex media-understanding provider, the provider returned parsed JSON on the bounded app-server path, the selected auth profile reached the provider-owned runtime, and the text-only call failed early with the intended image-required guard instead of widening this seam into general text extraction.
What was not tested: This proof intentionally uses a stubbed app-server client so it can exercise the real runtime/provider dispatch path deterministically in a local checkout without requiring a desktop-bound live OAuth session. The PR does not include a credentialed live Codex desktop turn artifact because that would require shipping private local auth/session material into public review evidence.
Validation
pnpm install --frozen-lockfilepnpm plugin-sdk:api:genpnpm plugin-sdk:api:checkpnpm test src/media-understanding/runtime.test.ts src/media-understanding/provider-registry.test.ts extensions/codex/media-understanding-provider.test.ts src/plugins/runtime/index.test.tspnpm check:changed
Changed files
CHANGELOG.md(modified, +1/-0)docs/.generated/plugin-sdk-api-baseline.sha256(modified, +2/-2)docs/plugins/architecture-internals.md(modified, +28/-0)docs/plugins/sdk-runtime.md(modified, +27/-0)docs/plugins/sdk-subpaths.md(modified, +1/-1)extensions/codex/media-understanding-provider.test.ts(modified, +160/-2)extensions/codex/media-understanding-provider.ts(modified, +198/-10)src/media-understanding/runtime-types.ts(modified, +25/-0)src/media-understanding/runtime.test.ts(modified, +155/-0)src/media-understanding/runtime.ts(modified, +40/-1)src/media-understanding/types.ts(modified, +41/-0)src/plugin-sdk/media-understanding-runtime.ts(modified, +2/-0)src/plugin-sdk/media-understanding.ts(modified, +5/-0)src/plugin-sdk/test-helpers/plugin-runtime-mock.ts(modified, +2/-0)src/plugins/runtime/index.test.ts(modified, +1/-0)src/plugins/runtime/index.ts(modified, +3/-0)src/plugins/runtime/types-core.ts(modified, +1/-0)
PR #75578: [plugin sdk] Add session action gateway protocol
- Repository: openclaw/openclaw
- Author: 100yenadmin
- State: closed | merged: False
- Link: https://github.com/openclaw/openclaw/pull/75578
Description (problem / solution / changelog)
Why this matters
OpenClaw plugins increasingly need typed actions that the host can expose and dispatch: approve a workflow, reopen a case, accept extracted data, retry a run, continue an agent after a human decision, or branch a workflow from a structured operator choice.
Without a generic seam, plugin authors end up with two bad options:
- invent bespoke gateway methods and one-off UI contracts for every product feature; or
- push product-specific action routers into OpenClaw core.
This PR adds the missing middle layer: plugins register typed session actions once, the Gateway exposes one stable dispatch method, and OpenClaw keeps ownership of schema validation, scope enforcement, stale-plugin safety, and client protocol/codegen.
What kinds of plugins this could support
This seam is intentionally generic. It can support many plugin families without adding plugin-specific action routes to core:
- Approval and review plugins: approve, reject, request changes, or continue the agent with a typed reason.
- Support and case-management plugins: escalate, close, reopen, request follow-up, or attach a structured disposition.
- CRM and sales plugins: mark contacted, accept extracted updates, hand off ownership, or snooze the next step.
- Task and workflow plugins: claim, retry, confirm completion, or branch the workflow from an operator choice.
- Ops and incident plugins: suppress an alert, rerun diagnostics, confirm mitigation, or start escalation.
- UI companion plugins: render action buttons or menus in Control UI, native Apple clients, CLI helpers, or external companion surfaces that all call back through the same Gateway seam.
The important part: the plugin defines the action semantics. OpenClaw owns dispatch, auth, and transport-level enforcement.
New SDK seam
This PR adds:
OpenClawPluginApi.registerSessionAction(...)- typed action context, payload schema, success/failure results, and optional
continueAgent plugins.sessionActionon the Gateway protocol plus regenerated Swift models- runtime scope classification that honors action-declared
requiredScopes
Example plugin usage:
api.registerSessionAction({
id: "approve",
description: "Approve the current workflow",
requiredScopes: ["operator.approvals"],
schema: {
type: "object",
properties: {
message: { type: "string" },
},
},
handler: ({ payload, sessionKey }) => ({
data: {
approved: true,
message: payload?.message,
...(sessionKey ? { sessionKey } : {}),
},
continueAgent: true,
}),
});Runtime architecture
flowchart LR
Plugin["Plugin register()"] --> Registry["Typed session action registry"]
Client["Gateway client / Control UI / native app / CLI"] --> RPC["plugins.sessionAction"]
RPC --> Guard["Schema validation + scope classification"]
Guard --> Dispatch["Registered plugin action handler"]
Dispatch --> Result["Typed success or failure payload"]
Result --> ClientThe action is registered once inside the plugin runtime and can then be dispatched from any client surface that speaks the Gateway protocol.
Safety and boundaries
This stays intentionally narrow:
- action ids, schemas, and duplicate registrations are validated at registration time
- payloads are validated before handler execution and malformed traffic is rejected
- dispatch enforces action-declared
requiredScopesinstead of treating every action like the same write lane - unresolved out-of-process callers fall back to the standard operator scope bundle instead of under-scoping to
operator.write - stale or unloaded plugins are blocked instead of dispatching into dead registry state
- the upstream ACP live-delivery / streaming defaults already on
upstream/mainstay preserved in this slice
This is a bounded typed action protocol, not arbitrary plugin-owned gateway method injection.
What changed
- Added
OpenClawPluginApi.registerSessionActionplus SDK exports for action registration, context, and result types. - Added registry storage, validation, duplicate detection, loader snapshot/restore support, and plugin test API/captured registration support.
- Added Gateway protocol schemas/types, generated Swift models, and
plugins.sessionActiondispatch. - Added dynamic Gateway scope classification so known actions keep their declared least-privilege scopes, while unresolved out-of-process callers fall back to the standard operator scope bundle instead of under-scoping to
operator.write. - Fixed the shared agent-event bridge teardown so duplicate
runtime.tsmodule instances do not double-dispatch pinned/active registry subscriptions. - Preserved the upstream ACP live-delivery / streaming default changes already on
upstream/main; this slice stays narrowly focused on typed session-action registration, dispatch, and scope enforcement. - Added focused contract coverage for registration validation, payload schema validation, typed success/failure result validation, dispatch auth, stale-plugin blocking, pinned-registry event fanout, and method-scope classification.
Relationship to adjacent seams
This is narrower than attachments, scheduled turns, SessionEntry projection, or run-context lifecycle. It only adds the typed session-action registration/dispatch protocol plus the host scope enforcement that makes it safe to call.
Non-goals
- Does not render UI on its own; clients still choose how to present actions.
- Does not replace generic plugin gateway methods or open arbitrary new RPC namespaces.
- Does not include host-mediated attachments, scheduled turns, derived path facts, SessionEntry projection, finalize retry/run-context lifecycle, or advanced workflow composition fixtures.
- Does not reopen ACP streaming/live-delivery defaults; it preserves the upstream behavior already on main.
Stack context
- Predecessors: #73384, #74483
- Docs split: #74853
- Foundation: #72287
Real behavior proof
Behavior or issue addressed:
This fixes the under-scoped least-privilege path for plugins.sessionAction without broadening the seam beyond the session-action protocol. Before this patch, out-of-process callers that could not resolve a local plugin registry entry fell back to operator.write, so actions that correctly declared scopes like operator.approvals failed before reaching the handler. After this patch, known actions still use their declared scopes, unresolved callers fall back to the standard operator scope bundle instead of under-scoping, dispatch still enforces requiredScopes, and typed payload/result validation still rejects malformed traffic.
Real environment tested:
Fresh loopback Gateway started from /Users/lume/openclaw-review-worktrees/pr-75578-rebase with a workspace-loaded proof plugin at /tmp/openclaw-pr75578-proof.4VP2Yu/workspace/.openclaw/extensions/session-action-proof. The runtime used HOME=/tmp/openclaw-pr75578-proof.4VP2Yu/home, OPENCLAW_WORKSPACE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/workspace, OPENCLAW_STATE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/state, and OPENCLAW_CONFIG_PATH=/tmp/openclaw-pr75578-proof.4VP2Yu/config/openclaw.json.
Exact steps or command run after this patch:
HOME=/tmp/openclaw-pr75578-proof.4VP2Yu/home OPENCLAW_WORKSPACE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/workspace OPENCLAW_STATE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/state OPENCLAW_CONFIG_PATH=/tmp/openclaw-pr75578-proof.4VP2Yu/config/openclaw.json pnpm openclaw plugins inspect session-action-proof --runtime --json
HOME=/tmp/openclaw-pr75578-proof.4VP2Yu/home OPENCLAW_WORKSPACE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/workspace OPENCLAW_STATE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/state OPENCLAW_CONFIG_PATH=/tmp/openclaw-pr75578-proof.4VP2Yu/config/openclaw.json pnpm openclaw gateway call plugins.sessionAction --json --params '{"pluginId":"session-action-proof","actionId":"approve","payload":{"message":"post-fix"}}'
HOME=/tmp/openclaw-pr75578-proof.4VP2Yu/home OPENCLAW_WORKSPACE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/workspace OPENCLAW_STATE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/state OPENCLAW_CONFIG_PATH=/tmp/openclaw-pr75578-proof.4VP2Yu/config/openclaw.json node --import tsx <<'NODE'
import { callGateway } from './src/gateway/call.ts';
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from './src/utils/message-channel.ts';
const result = await callGateway({
method: 'plugins.sessionAction',
params: {
pluginId: 'session-action-proof',
actionId: 'approve',
payload: { message: 'post-fix' },
},
token: 'proof-token',
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
mode: GATEWAY_CLIENT_MODES.BACKEND,
});
console.log(JSON.stringify(result, null, 2));
NODE
HOME=/tmp/openclaw-pr75578-proof.4VP2Yu/home OPENCLAW_WORKSPACE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/workspace OPENCLAW_STATE_DIR=/tmp/openclaw-pr75578-proof.4VP2Yu/state OPENCLAW_CONFIG_PATH=/tmp/openclaw-pr75578-proof.4VP2Yu/config/openclaw.json node --import tsx <<'NODE'
import { callGateway } from './src/gateway/call.ts';
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from './src/utils/message-channel.ts';
try {
await callGateway({
method: 'plugins.sessionAction',
params: {
pluginId: 'session-action-proof',
actionId: 'approve',
payload: { message: 42 },
},
token: 'proof-token',
clientName: GATEWAY_CLIENT_NAMES.GATEWAY_CLIENT,
mode: GATEWAY_CLIENT_MODES.BACKEND,
});
console.log('unexpected success');
} catch (error) {
console.error(String(error));
process.exitCode = 1;
}
NODEEvidence after fix:
$ pnpm openclaw gateway call plugins.sessionAction --json --params '{"pluginId":"session-action-proof","actionId":"approve","payload":{"message":"post-fix"}}'
gateway connect failed: GatewayClientRequestError: scope upgrade pending approval (requestId: f97fd499-4729-4cd9-b7e0-d7dc90334595)
Gateway call failed: GatewayTransportError: gateway closed (1008): pairing required: device is asking for more scopes than currently approved (requestId: f97fd499-4729-4cd9-b7e0-d7dc90334595)
$ node --import tsx <authorized backend proof>
{
"ok": true,
"result": {
"approved": true,
"message": "post-fix",
"sessionKey": null,
"scopes": [
"operator.admin",
"operator.read",
"operator.write",
"operator.approvals",
"operator.pairing",
"operator.talk.secrets"
]
}
}
$ node --import tsx <invalid payload proof>
GatewayClientRequestError: plugin session action payload does not match schema: message: must be stringObserved result after fix:
The paired CLI flow no longer under-scopes plugins.sessionAction to operator.write; it now requests the broader operator scope bundle and hits the existing scope-upgrade approval gate, which preserves pairing/approval enforcement instead of failing with a misleading handler-level missing-scope error. An authorized backend/token caller successfully dispatches the typed session action and receives the typed success payload. A malformed payload is rejected before handler execution with the expected schema error.
What was not tested: This proof did not run a full end-to-end Control UI or native Apple client invocation against the regenerated Swift models. The Swift surface was regenerated and covered at the protocol/codegen layer in this patch, but the live proof here is intentionally limited to real Gateway registration/dispatch/scope enforcement and typed payload validation.
Validation
pnpm protocol:genpnpm protocol:gen:swiftpnpm test src/gateway/method-scopes.test.ts src/gateway/call.test.ts src/agents/tools/gateway.test.ts src/plugins/contracts/session-actions.contract.test.tspnpm test src/plugins/runtime.channel-pin.test.tsOPENCLAW_VITEST_MAX_WORKERS=1 pnpm test src/plugins/runtime.test.ts src/plugins/runtime.channel-pin.test.tsgit diff --check
Changed files
CHANGELOG.md(modified, +1/-0)apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift(modified, +196/-0)docs/.generated/plugin-sdk-api-baseline.sha256(modified, +2/-2)scripts/protocol-gen-swift.ts(modified, +246/-1)src/agents/tools/gateway.test.ts(modified, +66/-0)src/agents/tools/gateway.ts(modified, +1/-1)src/gateway/call.test.ts(modified, +24/-0)src/gateway/call.ts(modified, +2/-2)src/gateway/method-scopes.test.ts(modified, +94/-0)src/gateway/method-scopes.ts(modified, +55/-1)src/gateway/protocol/index.ts(modified, +11/-0)src/gateway/protocol/schema/plugins.ts(modified, +35/-0)src/gateway/protocol/schema/protocol-schemas.ts(modified, +8/-0)src/gateway/protocol/schema/types.ts(modified, +2/-0)src/gateway/server-methods-list.ts(modified, +1/-0)src/gateway/server-methods/plugin-host-hooks.ts(modified, +314/-0)src/plugin-sdk/core.ts(modified, +5/-0)src/plugin-sdk/plugin-entry.ts(modified, +10/-0)src/plugin-sdk/plugin-test-api.ts(modified, +2/-0)src/plugins/agent-event-emission.ts(added, +84/-0)src/plugins/api-builder.ts(modified, +9/-0)src/plugins/captured-registration.ts(modified, +8/-0)src/plugins/contracts/session-actions.contract.test.ts(added, +1018/-0)src/plugins/host-hooks.ts(modified, +46/-0)src/plugins/loader.ts(modified, +3/-0)src/plugins/registry-empty.ts(modified, +1/-0)src/plugins/registry-lifecycle.ts(modified, +6/-0)src/plugins/registry-types.ts(modified, +10/-0)src/plugins/registry.ts(modified, +137/-2)src/plugins/runtime-state.ts(modified, +1/-0)src/plugins/runtime.channel-pin.test.ts(modified, +110/-0)src/plugins/runtime.test.ts(modified, +17/-0)src/plugins/runtime.ts(modified, +86/-24)src/plugins/schema-validator.ts(modified, +6/-4)src/plugins/types.ts(modified, +12/-0)
PR #75581: [plugin sdk] Add host-mediated session attachments
- Repository: openclaw/openclaw
- Author: 100yenadmin
- State: open | merged: False
- Link: https://github.com/openclaw/openclaw/pull/75581
Description (problem / solution / changelog)
Why this matters
OpenClaw plugins increasingly need to send real files back to the user in the same session lane: support artifacts, generated reports, approval docs, exports, screenshots, and workflow evidence.
Without a generic seam, plugin authors end up with two bad options:
- own Telegram/Slack/Discord delivery logic themselves, including route lookup and transport quirks; or
- push product-specific attachment routes into OpenClaw core.
This PR adds the missing middle layer: a bundled plugin can ask the host to validate local files and deliver them through the active direct-outbound session route, while OpenClaw keeps ownership of channel credentials, delivery routing, formatting quirks, and transport behavior.
What kinds of plugins this could support
This seam is intentionally generic. It can support many plugin families without adding plugin-specific delivery logic to core:
- Support and incident plugins: send logs, screenshots, repro bundles, or diagnostics back into the live support conversation.
- Approval and review plugins: attach generated PDFs, redlines, or diffs into the same operator thread that requested them.
- CRM and case-management plugins: deliver quote files, customer evidence, or case exports to the active session route.
- Field ops and inspection plugins: return photos, signed forms, or inspection reports to the same conversation lane.
- Migration and import plugins: send rejected-record CSVs or validation reports back to a human operator.
- Research and reporting plugins: attach exported summaries or evidence files instead of pasting large blobs into chat.
The important part: the plugin decides what artifact to send. OpenClaw decides whether the file is safe and where the active session route actually goes.
New SDK seam
This PR adds:
OpenClawPluginApi.sendSessionAttachment(...)- typed attachment params and result types in the SDK
- host-side file validation, size/count limits, session-route lookup, and stale-registry protection
- workspace-relative resolution against the session agent workspace
- channel hints for caption formatting, Telegram delivery behavior, and Slack thread targeting
Example plugin usage:
const result = await api.sendSessionAttachment({
sessionKey: "agent:main:main",
files: [{ path: "./proof-report.txt" }],
text: "attachment ready",
captionFormat: "plain",
});Runtime architecture
flowchart LR
Plugin["Plugin callback, handler, or workflow"] --> SDK["sendSessionAttachment(...)"]
SDK --> Validate["Host file validation and workspace resolution"]
Validate --> Route["Active session route lookup"]
Route --> Channel["Direct outbound channel adapter"]
Channel --> User["Delivered attachment in the live session lane"]The plugin never talks directly to Telegram, Slack, or another channel transport. It asks the host to deliver a validated file to the already-active session route.
Safety and boundaries
This stays intentionally narrow:
- bundled-only: this seam depends on host-managed session and channel integrations
- the host validates file count, size, path, and optional delivery hints before calling the outbound adapter
- relative paths resolve against the session agent workspace instead of whatever host cwd happens to be active
- delivery stays bound to the active session route; plugins do not get arbitrary channel send power
- stale or unloaded plugin registries are rejected instead of dispatching through dead state
- channel hints stay hints; plugins do not take raw provider or credential ownership
This is a host-mediated attachment seam, not a generic channel SDK.
What changed
- Added
OpenClawPluginApi.sendSessionAttachmentplus SDK exports for attachment params and results. - Added host-side file validation, delivery-route lookup hardening, bundled-plugin enforcement, and stale-registry protection.
- Resolved relative file paths against the session agent workspace so real plugin callbacks do not fall back to the default host workspace.
- Added channel attachment hints from #74483: caption-format precedence, Telegram silent/parse-mode/force-document hints, and Slack thread targeting.
- Plumbed
parseModethrough outbound message formatting and the Telegram direct/gateway adapters. - Re-exported Telegram formatting helpers for plugin authors that need Telegram-safe captions.
- Preserved the loader's closed-after-register guard for registration-only APIs, but kept
sendSessionAttachmentcallable afterregister()so a real bundled plugin can capture it during registration and invoke it later from a live callback or handler. - Added focused contract and loader regression coverage for stale-registry protection, workspace-relative resolution, direct route dispatch, and post-registration late calls.
Relationship to adjacent seams
This does not try to solve scheduling, session actions, session projection, or run-context lifecycle. It is deliberately narrower: one host-mediated path for delivering plugin-owned files through the active session route.
Non-goals
- Does not give plugins direct Telegram, Slack, or Discord delivery ownership.
- Does not add arbitrary channel broadcast or free-form outbound routing.
- Does not include scheduled turns, session actions, derived path facts, SessionEntry projection, finalize retry/run-context lifecycle, or advanced workflow composition fixtures.
- Does not claim to close broader Control UI/native-page RFC scope; this only adds the generic attachment delivery seam.
Stack context
- Predecessors: #73384, #74483
- Docs split: #74853
- Foundation: #72287
Real behavior proof
Behavior or issue addressed:
This fixes the narrow host-mediated attachment seam without broadening it into direct channel ownership. Before these follow-up fixes, a captured sendSessionAttachment closure could be closed accidentally by the loader guard, relative attachment paths could resolve against the wrong workspace, and Telegram media captions could be forced through the HTML path even when HTML was not explicitly requested. After this patch, the loader keeps sendSessionAttachment callable after register(), the host resolves relative paths against the session agent workspace, and the active direct-outbound route receives the validated file plus caption text.
Real environment tested:
Standalone runtime proof from /Users/lume/openclaw-review-worktrees/pr-75581-rebase using a live session store, an agent workspace rooted in a fresh temp state directory, and a direct outbound proof channel registered in the active plugin registry.
Exact steps or command run after this patch:
node --import tsx <<'NODE'
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { updateSessionStore } from './src/config/sessions.js';
import { sendPluginSessionAttachment } from './src/plugins/host-hook-workflow.js';
import { setActivePluginRegistry, getActivePluginRegistry } from './src/plugins/runtime.js';
import { createTestRegistry } from './src/test-utils/channel-plugins.js';
const previousRegistry = getActivePluginRegistry();
const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), 'openclaw-pr75581-proof-'));
const workspaceDir = path.join(stateDir, 'workspace');
const storePath = path.join(stateDir, 'sessions.json');
await fs.mkdir(workspaceDir, { recursive: true });
await fs.writeFile(path.join(workspaceDir, 'proof-report.txt'), 'proof attachment body\n', 'utf8');
await updateSessionStore(storePath, (store) => {
store['agent:main:main'] = {
sessionId: 'session-id',
updatedAt: Date.now(),
deliveryContext: { channel: 'proofchat', to: '12345', accountId: 'default' },
};
return undefined;
});
const deliveries = [];
setActivePluginRegistry(
createTestRegistry([
{
pluginId: 'proofchat',
source: 'proof',
plugin: {
id: 'proofchat',
meta: {
id: 'proofchat',
label: 'Proof Chat',
selectionLabel: 'Proof Chat',
docsPath: '/channels/proofchat',
blurb: 'proof direct channel',
},
capabilities: { chatTypes: ['direct'] },
config: {
listAccountIds: () => ['default'],
resolveAccount: () => ({ accountId: 'default' }),
},
outbound: {
deliveryMode: 'direct',
sendText: async ({ to, text, accountId }) => {
deliveries.push({ to, text, mediaUrl: null, accountId: accountId ?? null });
return { channel: 'proofchat', messageId: 'proof-text-1' };
},
sendMedia: async ({ to, text, mediaUrl, accountId }) => {
deliveries.push({ to, text, mediaUrl: mediaUrl ?? null, accountId: accountId ?? null });
return { channel: 'proofchat', messageId: 'proof-media-1' };
},
},
},
},
]),
);
const result = await sendPluginSessionAttachment({
origin: 'bundled',
sessionKey: 'agent:main:main',
files: [{ path: './proof-report.txt' }],
text: 'attachment ready',
config: {
session: { store: storePath },
agents: { list: [{ id: 'main', workspace: workspaceDir }] },
},
});
console.log(JSON.stringify({ result, deliveries }, null, 2));
setActivePluginRegistry(previousRegistry);
NODEEvidence after fix:
{
"result": {
"ok": true,
"channel": "proofchat",
"deliveredTo": "12345",
"count": 1
},
"deliveries": [
{
"to": "12345",
"text": "attachment ready",
"mediaUrl": "/var/folders/l7/tyvn8qfs50v57w__75s3k0f00000gp/T/openclaw-pr75581-proof-pHeAVB/workspace/proof-report.txt",
"accountId": "default"
}
]
}Observed result after fix:
The bundled-only attachment dispatch succeeds, the relative attachment path resolves against the session agent workspace instead of falling back to the default host workspace, and the active direct-outbound route receives the resolved file plus caption text. The loader regression is also covered on this head by pnpm test src/plugins/loader.test.ts, which proves sendSessionAttachment stays callable after register() while registration-only APIs still close.
What was not tested: This proof did not exercise a full external Telegram/Slack/Discord live account or an end-to-end GUI flow. The runtime proof here is intentionally limited to real host validation, session-route lookup, workspace-relative path resolution, and direct outbound attachment dispatch inside a live OpenClaw runtime.
Validation
pnpm test src/plugins/loader.test.tspnpm test src/config/sessions/delivery-info.test.ts src/plugins/contracts/session-attachments.contract.test.ts extensions/telegram/api.test.ts extensions/telegram/src/outbound-adapter.test.ts extensions/telegram/src/channel.gateway.test.tspnpm plugin-sdk:api:checkpnpm lint:corepnpm lint:extensionsgit diff --check
Changed files
CHANGELOG.md(modified, +1/-0)apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift(modified, +12/-0)docs/.generated/plugin-sdk-api-baseline.sha256(modified, +2/-2)docs/plugins/sdk-overview.md(modified, +1/-0)extensions/googlechat/config-api.ts(added, +2/-0)extensions/googlechat/src/config-schema.ts(modified, +1/-1)extensions/matrix/src/matrix/monitor/handler.test-helpers.ts(modified, +2/-1)extensions/matrix/src/matrix/monitor/handler.test.ts(modified, +2/-0)extensions/telegram/api.test.ts(added, +16/-0)extensions/telegram/api.ts(modified, +8/-0)extensions/telegram/src/channel.gateway.test.ts(modified, +47/-0)extensions/telegram/src/format.ts(modified, +6/-2)extensions/telegram/src/outbound-adapter.test.ts(modified, +5/-6)extensions/telegram/src/outbound-adapter.ts(modified, +12/-2)src/config/sessions/delivery-info.test.ts(modified, +223/-3)src/config/sessions/delivery-info.ts(modified, +154/-24)src/gateway/protocol/schema/agent.ts(modified, +6/-0)src/gateway/server-methods/send.test.ts(modified, +22/-0)src/gateway/server-methods/send.ts(modified, +6/-0)src/infra/outbound/formatting.ts(modified, +1/-0)src/infra/outbound/message.channels.test.ts(modified, +17/-0)src/infra/outbound/message.ts(modified, +6/-0)src/plugin-sdk/core.ts(modified, +2/-0)src/plugin-sdk/plugin-entry.ts(modified, +4/-0)src/plugin-sdk/plugin-test-api.ts(modified, +1/-0)src/plugins/api-builder.ts(modified, +6/-0)src/plugins/captured-registration.ts(modified, +1/-0)src/plugins/contracts/bundled-extension-config-api-guardrails.test.ts(modified, +1/-1)src/plugins/contracts/session-attachments.contract.test.ts(added, +877/-0)src/plugins/host-hook-workflow.ts(added, +305/-0)src/plugins/host-hooks.ts(modified, +41/-0)src/plugins/loader.test.ts(modified, +61/-0)src/plugins/loader.ts(modified, +9/-1)src/plugins/registry.ts(modified, +32/-0)src/plugins/types.ts(modified, +15/-0)
PR #75588: [plugin sdk] Add scheduled session turns
- Repository: openclaw/openclaw
- Author: 100yenadmin
- State: open | merged: False
- Link: https://github.com/openclaw/openclaw/pull/75588
Description (problem / solution / changelog)
Why this matters
OpenClaw plugins increasingly need to create follow-up work that happens later, in the same session lane the user is already operating in: remind me in an hour, reopen this investigation tomorrow, schedule a follow-up agent turn after an approval window, or clean up a family of pending nudges when the workflow resolves.
Without a generic seam, plugin authors end up with two bad options:
- push product-specific scheduling logic into OpenClaw core; or
- own host cron details themselves, which leaks infrastructure concerns into every plugin.
This PR adds the narrow host-scheduler seam in the plugin SDK. Plugins can ask the host to schedule a future session turn and later clean up plugin-owned scheduled jobs by tag, while OpenClaw keeps ownership of cron naming, lifecycle, delivery routing, and cleanup semantics.
What kinds of plugins this could support
This seam is intentionally generic. It can support many plugin families without adding plugin-specific scheduling routes to core:
- Support and ops plugins: reopen or re-check incidents after a delay, schedule escalation nudges, or queue a follow-up diagnostic turn.
- CRM and sales plugins: create reminder turns for deal follow-ups, pending replies, or renewal check-ins.
- Approval and workflow plugins: schedule reminders while waiting on a human decision, then clean up all related nudges when the workflow resolves.
- Digest and assistant plugins: schedule a future turn that produces a recap, check-in, or status refresh in the same session lane.
- Import, migration, and reconciliation plugins: retry or re-check long-running work later without inventing a parallel scheduler stack.
- Research and monitoring plugins: schedule a revisit turn after some cooling-off period when an external dependency is expected to change.
The important part: the plugin decides why the follow-up exists. OpenClaw only provides the bounded host seam for when it should happen and how it is cleaned up.
New SDK seam
This PR adds:
OpenClawPluginApi.scheduleSessionTurn(...)OpenClawPluginApi.unscheduleSessionTurnsByTag(...)- typed schedule / cleanup params and result types in the SDK
- host-side support for
at,delayMs, and cron schedules - plugin-prefixed cron naming plus plugin-owned tag cleanup
- a narrow post-registration carve-out so only these two helpers remain callable after
register()
Example plugin usage:
const reminder = await api.scheduleSessionTurn({
sessionKey,
delayMs: 60 * 60 * 1000,
text: "Re-check whether the customer replied.",
tag: "followup",
deliveryMode: "announce/last",
deleteAfterRun: true,
});
await api.unscheduleSessionTurnsByTag({
sessionKey,
tag: "followup",
});Runtime architecture
flowchart LR
Plugin["Plugin callback, handler, or workflow"] --> SDK["scheduleSessionTurn / unscheduleSessionTurnsByTag"]
SDK --> HostHooks["Host hook workflow"]
HostHooks --> Cron["Host cron naming and persistence"]
Cron --> FutureTurn["Future agent turn in the same session lane"]
Plugin --> Cleanup["Plugin-owned tag cleanup"]
Cleanup --> CronThe plugin owns the follow-up intent and tag taxonomy. OpenClaw owns the cron contract, namespacing, and actual future-turn delivery.
Safety and boundaries
This stays intentionally narrow:
- plugins do not get generic cron ownership or arbitrary background-job control
- only
scheduleSessionTurnandunscheduleSessionTurnsByTagstay callable afterregister() - cron job names are namespaced by plugin id so short tags do not clobber each other
- reserved cleanup tags are rejected instead of silently widening semantics
- invalid
cron + deleteAfterRun: truecombinations are rejected instead of guessing - scheduled-turn records stay plugin-owned until lifecycle cleanup confirms the host job is gone
This is a scheduler seam for future session turns, not a generic host automation API.
What changed
- Added
OpenClawPluginApi.scheduleSessionTurnandunscheduleSessionTurnsByTagplus SDK exports for schedule/cleanup params and results. - Added host cron payload construction for
at,delayMs, and cron schedules, with optional delivery mode. - Added plugin-prefixed cron names and tag cleanup so two plugins can reuse short tags without clobbering each other.
- Rejected reserved
:cleanup tags and rejectedcron + deleteAfterRun: trueinstead of silently widening the host contract. - Preserved plugin-owned scheduled-turn records until lifecycle cleanup confirms the host job is gone, instead of time-pruning those records early.
- Preserved the loader's closed-after-register guard for normal registration mutations, but let only
scheduleSessionTurnandunscheduleSessionTurnsByTagremain callable afterregister()so a real bundled plugin can capture them duringregister()and invoke them later from a registered callback/handler. - Added real-loader regression coverage for post-registration gateway-handler dispatch plus the invalid-tag / invalid-deleteAfterRun cases.
- Moved the changelog entry back under
## Unreleasedand regenerated the plugin-sdk API baseline after the rebase.
Relationship to adjacent seams
This does not try to solve every workflow problem in one slice. It is intentionally narrower than session actions, run-context lifecycle, attachments, or SessionEntry projection. Those remain split into follow-up PRs so maintainers can review one host seam at a time.
Non-goals
- Does not widen the generic post-registration plugin API beyond these two operational helpers.
- Does not give plugins arbitrary cron ownership or a general background-job runner.
- Does not include session actions, host-mediated attachments, finalization retry/run-context lifecycle, tool-derived paths, SessionEntry projection, or advanced workflow composition fixtures.
- Does not claim to close broader Control UI/native-page RFC scope; this only adds the generic scheduled-turn seam.
Real behavior proof
Behavior or issue addressed:
This keeps the post-registration seam narrow while proving the scheduled-turn helpers work in a real host runtime. After this patch, a bundled plugin can capture scheduleSessionTurn and unscheduleSessionTurnsByTag during register(), invoke them later from a registered callback/handler, create real host cron jobs, reject invalid cleanup inputs without widening the contract, and clean up the tagged jobs deterministically.
Real environment tested:
On 2026-05-09, I started a real loopback gateway from /Users/lume/openclaw-review-worktrees/pr-75588-rebase and loaded a real bundled proof-scheduler plugin through loadOpenClawPlugins.
Exact steps or command run after this patch:
- Loaded the bundled
proof-schedulerplugin and let it captureapi.scheduleSessionTurnplusapi.unscheduleSessionTurnsByTagduringregister(). - Invoked the registered plugin gateway handler
proof-scheduler.exerciseso the plugin exercised those captured helpers after registration. - Called
cron.listimmediately after scheduling to inspect the real host jobs. - Ran the cleanup phase through
unscheduleSessionTurnsByTag, then calledcron.listagain to verify that the tagged jobs were gone.
Evidence after fix:
- The schedule phase returned two real handles:
7d2d8bf2-f028-4a44-ad08-c75fd966b965ca448912-25a0-4aec-9e87-88587464e0dc
- The invalid
tag: "bad:tag"case returnednull. - The invalid
cron + deleteAfterRun: truecase returnednull. cron.listimmediately after schedule showed two real host jobs namedplugin:proof-scheduler:tag:proof:agent:main:main:*, both targetingsession:agent:main:main, bothdeleteAfterRun: true, with delivery modesannounce/lastandnone, and with scheduledattimestamps2026-05-09T17:08:00.334Zand2026-05-09T17:09:00.356Z.- The cleanup phase returned
{ removed: 2, failed: 0 }. cron.listafter cleanup returned[].
Copied live output after fix:
$ proof-scheduler.exercise
first.id=7d2d8bf2-f028-4a44-ad08-c75fd966b965
second.id=ca448912-25a0-4aec-9e87-88587464e0dc
badTag=null
badDelete=null
$ cron.list
plugin:proof-scheduler:tag:proof:agent:main:main:* deleteAfterRun=true delivery=announce/last at=2026-05-09T17:08:00.334Z
plugin:proof-scheduler:tag:proof:agent:main:main:* deleteAfterRun=true delivery=none at=2026-05-09T17:09:00.356Z
$ unscheduleSessionTurnsByTag
{ removed: 2, failed: 0 }
$ cron.list
[]Observed result after fix:
The real bundled plugin was able to use the two late-call helpers after register() without reopening the rest of the plugin API. Real host cron jobs were created, visible in cron.list, and then removed cleanly by the tagged cleanup path. Invalid cleanup tags and invalid cron + deleteAfterRun: true combinations were rejected instead of silently widening the contract.
What was not tested: This proof did not cover a full user-facing UI flow or a long-lived production cron lifecycle across process restarts. The live proof here is intentionally limited to real post-registration helper capture, real host cron job creation/listing, invalid-input rejection, and tagged cleanup in a live OpenClaw runtime.
Validation
pnpm test src/plugins/contracts/scheduled-turns.contract.test.ts src/plugins/contracts/host-hooks.contract.test.tspnpm plugin-sdk:api:genpnpm plugin-sdk:api:checkpnpm lint:coregit diff --checknpm run check:no-conflict-markers
Changed files
CHANGELOG.md(modified, +1/-0)docs/.generated/plugin-sdk-api-baseline.sha256(modified, +2/-2)src/plugin-sdk/core.ts(modified, +3/-0)src/plugin-sdk/plugin-entry.ts(modified, +6/-0)src/plugin-sdk/plugin-test-api.ts(modified, +2/-0)src/plugins/api-builder.ts(modified, +8/-0)src/plugins/captured-registration.test.ts(modified, +47/-1)src/plugins/captured-registration.ts(modified, +18/-5)src/plugins/contracts/host-hooks.contract.test.ts(modified, +57/-0)src/plugins/contracts/scheduled-turns.contract.test.ts(added, +1216/-0)src/plugins/host-hook-cleanup.ts(modified, +3/-0)src/plugins/host-hook-runtime.ts(modified, +18/-2)src/plugins/host-hook-workflow.ts(added, +481/-0)src/plugins/host-hooks.ts(modified, +40/-0)src/plugins/loader.ts(modified, +14/-0)src/plugins/registry.ts(modified, +47/-0)src/plugins/types.ts(modified, +20/-0)
Code Example
flowchart TD
A["OpenClaw Plugin SDK Today"] --> B["Flat OpenClawPluginApi"]
A --> C["302 shipped plugin-sdk subpaths"]
B --> D["declaration-time registration"]
B --> E["setup/bootstrap-only seams"]
B --> F["host workflow and session control"]
B --> G["trusted runtime escape hatch"]
H["Target Model"] --> I["kernel"]
H --> J["capabilities"]
H --> K["workflow"]
H --> L["client-contrib"]
H --> M["runtime-facades"]
H --> N["compat"]
H --> O["bundled-internal"]
H --> P["memory"]
K --> Q["session.state"]
K --> R["session.workflow"]
K --> S["agent.events"]
K --> T["runContext"]
K --> U["lifecycle"]
L --> V["session.controls"]RAW_BUFFERClick to expand / collapse
OpenClaw Plugin SDK Whole-Surface Architecture Plan
Date: 2026-05-10
Status: Whole-surface audit complete
Confidence: 95%+
Repo: /Volumes/LEXAR/repos/openclaw-plugin-sdk-consolidation
Executive Summary
OpenClaw already has a real plugin-first kernel. The strongest parts are:
- manifest-first discovery and activation planning
- registry-based capability registration
- typed hooks
- shared channel/provider kernels
- session workflow state seams
The core problem is not "plugins are the wrong direction." The core problem is that the current public SDK surface mixes too many classes of API at the same level:
- declaration-time registration
- setup/bootstrap-only discovery
- live session/workflow control
- runtime helper facades
- bundled-only authority seams
- compatibility and migration debt
So the right cleanup is not to smash everything into one generic API. The right cleanup is:
- classify the whole shipped surface from one source of truth
- make lifecycle semantics explicit
- make grouped families canonical
- freeze and de-emphasize wide barrels and compat shims
- internalize bundled-only and semi-internal subsystems where the repo already treats them as special
This preserves features while reducing accidental permanent API.
What This Audit Covered
- the full
OpenClawPluginApishape insrc/plugins/types.ts - the root
openclawpackage export map foropenclaw/plugin-sdk/* - docs and generated-baseline surfaces
- repo-wide import usage across extensions, tests, docs, and runtime code
- internal lifecycle enforcement around post-register callability
- compatibility and plugin-owned/public classifications
This was informed by direct repo inspection plus four independent read-only subagent passes over:
- lifecycle and role classification
- export/publicness drift
- architectural taxonomy and boundaries
- real in-repo usage and blast radius
Current Surface Snapshot
Counts
OpenClawPluginApicurrently exposes79top-level fields/methods.- Root
package.jsoncurrently publishes305exports total. 302of those are./plugin-sdk*entrypoints.376non-testsrc/plugin-sdk/*.tsmodules exist in-tree.- Rough file-family distribution of those non-test plugin-sdk modules:
131includeruntime59are channel-related40are provider-related23are test-ish/testing/contract/mocks21are memory-related4are compat/legacy-marked
255uniqueplugin-sdk/*subpaths are explicitly mentioned indocs/plugins/sdk-subpaths.md.48shipped plugin-sdk exports are not catalogued there.39shipped plugin-sdk exports had zero in-repo imports in the static scan.
Strongest Stable Usage
The highest-blast-radius shipped subpaths in production-ish extension code are:
text-runtimeconfig-typesruntime-envplugin-entryprovider-model-sharedchannel-contractchannel-messageroutingprovider-authprovider-http
The highest-use consumer families are channel/plugin stacks like:
discordtelegramslackmatrixwhatsapp
That means many apparently generic helpers are really carrying mature channel-stack behavior and should not be casually churned.
Low-Risk Consolidation Signals
- The new grouped aliases in
api.session.*,api.agent.*,api.runContext.*, andapi.lifecycle.*are basically docs/tests/facade-only today, with no meaningful in-repo author adoption yet. openclaw/plugin-sdk/compatis effectively dead in-repo.- the bare root
openclaw/plugin-sdkimport behaves mostly like a migration or test surface, not a healthy primary authoring surface - many host-workflow/session-control registrars are still mostly fixture and contract-level seams, not widely used bundled-plugin author APIs
Those are exactly the conditions that make canonicalization and de-emphasis safe.
Core Diagnosis
Two structural problems explain most of the SDK sprawl.
1. Publication-by-list, not publication-by-policy
The public package contract is mechanically generated from the entrypoint list. That makes it easy to ship new subpaths, but harder to keep "exported", "documented", "supported", "compat-only", and "bundled-only" aligned.
Today there are multiple overlapping policy layers:
- the root package export map
plugin-sdk-entrypoints.jsonplugin-sdk/entrypoints.tsclassification arraysdocs/plugins/sdk-subpaths.mdpluginSdkDocMetadata- boundary/contract tests
That is workable, but it is not one authoritative support model.
2. Lifecycle flattening
OpenClawPluginApi currently flattens at least four different kinds of API into
one top-level type:
- declaration-time registration
- setup-only/bootstrap discovery
- live workflow/session control
- trusted in-process runtime power
That flattening makes the API feel broader and more inconsistent than it needs to be. It also explains why reviewer discomfort shows up as "the API keeps expanding" even when some of the seams are individually valid.
Architectural Thesis
The long-term SDK should read as a small number of explicit families:
kernelcapabilitiesworkflowclient-contribruntime-facadescompatbundled-internalmemory
The first four are the plugin-first public platform. The last four need much stronger curation and labeling.
Target Taxonomy
flowchart TD
A["OpenClaw Plugin SDK Today"] --> B["Flat OpenClawPluginApi"]
A --> C["302 shipped plugin-sdk subpaths"]
B --> D["declaration-time registration"]
B --> E["setup/bootstrap-only seams"]
B --> F["host workflow and session control"]
B --> G["trusted runtime escape hatch"]
H["Target Model"] --> I["kernel"]
H --> J["capabilities"]
H --> K["workflow"]
H --> L["client-contrib"]
H --> M["runtime-facades"]
H --> N["compat"]
H --> O["bundled-internal"]
H --> P["memory"]
K --> Q["session.state"]
K --> R["session.workflow"]
K --> S["agent.events"]
K --> T["runContext"]
K --> U["lifecycle"]
L --> V["session.controls"]Kernel
Keep as the architectural center:
- manifest/discovery
- entrypoints
- activation planning
- registration modes
- narrow import discipline
This is the cleanest and healthiest part of the system.
Capabilities
These are the true foundational public primitives:
- provider registration
- channel registration
- tool registration
- command registration
- route registration
- CLI registration
- service registration
- discovery registration
These should stay public and canonical.
Workflow
These are real plugin-first app behavior primitives:
- session extensions
- next-turn injections
- run context
- lifecycle cleanup
- event subscription/emission
- scheduler ownership and session workflow helpers
These should stay public, but the grouped family shape should become the main story.
Client-Contrib
These are useful, but they are not kernel primitives:
- control UI descriptors
- typed session actions
They should read as client-extension contracts, not as generic platform verbs.
Runtime-Facades
These should remain narrow and cheap:
- channel/provider/runtime helper subpaths
- utility leaves used by bundled and external plugins
They should not become a backdoor to host internals or another broad pseudo-root SDK.
Compat
These are transitional:
compattestingchannel-runtimeinfra-runtime- branded compatibility facades
- legacy reply/runtime shims
They should remain supported where needed, but visibly not be peers of the kernel.
Bundled-Internal
These are real seams, but they are not healthy third-party platform primitives:
- trusted tool policy
- agent tool result middleware
- Codex app-server extension factories
- host-owned authority surfaces
- experimental agent-harness-level seams
They should be labeled and treated as advanced/bundled-native, not casually public.
Memory
Memory needs special treatment. The exclusive capability idea is good, but much of the current memory-host SDK family is still a semi-internal bridge dressed as public SDK. This is the strongest whole-surface internalization candidate.
Whole-Surface Classification
Keep Public and Canonical
definePluginEntry(...)- capability registrars
api.on(...)- tool/command/http/gateway/service/CLI/channel registration
- migration provider registration
registerMemoryCapability(...)- additive memory supplements
- manifest-first activation planning model
- narrow high-value leaves like
plugin-entry,config-types,routing,channel-contract,channel-message,provider-model-shared,provider-auth,provider-http
Keep Public but Reclassify as Families
registerSessionExtensionenqueueNextTurnInjectionregisterRuntimeLifecycleregisterAgentEventSubscriptionemitAgentEventsetRunContext/getRunContext/clearRunContextregisterSessionSchedulerJobregisterSessionActionregisterControlUiDescriptorsendSessionAttachmentscheduleSessionTurnunscheduleSessionTurnsByTag
These should remain available, but the grouped family model should become canonical:
api.session.state.*api.session.workflow.*api.session.controls.*api.agent.events.*api.runContext.*api.lifecycle.*
Fold or Alias
registerHook(...)-> legacy alias toapi.on(...)registerNodeCliFeature(...)-> helper overregisterCli(...)registerMemoryPromptSection(...)-> alias toregisterMemoryCapability(...)registerMemoryFlushPlan(...)-> alias toregisterMemoryCapability(...)registerMemoryRuntime(...)-> alias toregisterMemoryCapability(...)- flat host-workflow/control/run-context methods -> compatibility aliases over grouped family entrypoints
Freeze and De-Emphasize
Do not keep growing these as primary design targets:
- bare root
openclaw/plugin-sdk compattestingchannel-runtimeinfra-runtimeconfig-runtime- wide umbrellas like
core,reply-runtime,media-runtime,text-runtime,agent-runtime,plugin-runtime
These can stay shipped, but should be treated as supported umbrellas or compat, not as the best place for new API design.
Advanced / Bundled-Only / Internalize
Strong internalization or advanced-only candidates:
runtimeregisterTrustedToolPolicy(...)registerAgentToolResultMiddleware(...)registerCodexAppServerExtensionFactory(...)registerAgentHarness(...)registerContextEngine(...)registerCompactionProvider(...)registerDetachedTaskRuntime(...)- session attachment and scheduled-turn authority seams, insofar as they depend on host-owned trust/runtime lanes
- the broad
memory-core-host-*andmemory-host-*entrypoint families until they are truly package-owned and independently defensible as public SDK
Deprecate Harder
openclaw/plugin-sdk/compat- bare
openclaw/plugin-sdkroot import - branded facades that are explicitly transitional
- wide reply/channel compat bridges where the repo already prefers narrower leaves
Lifecycle Model
This needs to become explicit in code, docs, and tests.
Proposed Lifecycle Classes
declaration- registration-only, valid only during
register(api)
- registration-only, valid only during
setup_only- early discovery/bootstrap-only seams
late_call_live- may be called after
register()closes through the guarded API
- may be called after
active_loaded_live- live only when a plugin is loaded/active and the host runtime is present
bundled_internal- trusted or authority-bearing seam not intended as ordinary third-party API
Important Current Inconsistencies
activate(api)is not a true second lifecycle phase; loader effectively aliases it toregister- only
sendSessionAttachment,scheduleSessionTurn, andunscheduleSessionTurnsByTagare explicitly late-callable today emitAgentEventand run-context methods look like runtime handles, but they are not generally treated as late-callable by the guarded loader pathregisterConfigMigrationandregisterAutoEnableProbesit on the main API but are only meaningful in setup-only moderegisterCliandregisterChannelhave special wiring characteristics- some concerns exist as both entry-definition fields and runtime API methods, which blurs declaration-only vs registrar semantics
This is why lifecycle metadata should be authoritative and loader-enforced.
Safe Start Order
Phase 0: Governance, not churn
-
Add one authoritative SDK surface manifest. Each shipped export and each
OpenClawPluginApimethod should declare:- status:
stable,supported_umbrella,compat,plugin_owned_public,experimental,bundled_internal, orinternal_only - lifecycle class
- docs class/category
- owner, if plugin-owned or bundled-only
- status:
-
Generate these from that manifest:
- root package exports
- docs catalog
- API baseline
- boundary report
- deprecation report
-
Freeze new broad barrels and new top-level live verbs.
This is the highest-value first step because it reduces future API sprawl without breaking any current features.
Phase 1: Canonicalize the public story
- Make grouped host-workflow families canonical in docs and examples.
- Keep flat methods as compatibility aliases.
- Make
api.on(...)the canonical hook registration story. - Reclassify
registerNodeCliFeature(...)as a CLI helper, not a peer primitive. - Collapse the deprecated memory trio into
registerMemoryCapability(...).
This is low-risk because repo adoption of the grouped families is still minimal.
Phase 2: Make lifecycle semantics real
- Replace the current tiny post-register allowlist with authoritative method lifecycle metadata.
- Split setup-only surfaces from ordinary registration semantics in docs and policy.
- Clarify which surfaces are:
- declarative
- retained runtime handles
- bundled-only authority seams
Phase 3: Export-surface cleanup
- Review the undocumented exported subpaths first.
- Decide one of:
- document as intentional
- classify as compat
- internalize
Priority review set:
- root
plugin-sdk compatconfig-runtimechannel-runtimesandboxruntime-doctoragent-harness-runtimeprovider-stream-sharedchannel-secret-basic-runtime
- Internalize anything useful in-tree but not truly intended for external plugin authors, using the same pattern the repo already uses for QA-only surfaces.
Phase 4: Subsystem cleanup
- Memory-host surface review and likely shrink/internalization
- bundled facade review
- channel/reply/runtime bridge cleanup
- follow-on rationalization of wide umbrellas
What This Means for #80219 and #80229
#80219
#80219 should become the umbrella whole-surface issue, not just a note about
the four active PR seams. The current issue direction is good, but it should be
reframed as the architecture plan for:
- whole-surface classification
- lifecycle cleanup
- public vs compat vs bundled-only policy
- export and docs generation policy
#80229
#80229 should be framed as a valid Phase 1 slice, not as "the" Plugin SDK
cleanup.
Its best architectural value is:
- grouped host-workflow aliases
- narrow late-callability metadata
- proof-backed session/workflow/media seams
That is real progress, but it only addresses one part of the whole SDK surface.
Explicit Non-Goals
- do not collapse the SDK into one generic API
- do not widen generic
runtime.llm.complete(...)into the structured media-understanding seam - do not remove compatibility shims before policy classification exists
- do not rewrite high-blast-radius channel/provider helpers casually
- do not treat bundled-only trust surfaces as healthy third-party primitives
Bottom Line
The highest-confidence architecture plan is:
- keep manifest-first kernel and capability registration as the center
- keep workflow/session seams, but group them and label them clearly
- stop treating every exported helper as equal public platform API
- separate stable public primitives from compat, bundled-only, and semi-internal lanes
- use one authoritative surface manifest to govern exports, docs, baselines, and lifecycle policy
In one sentence:
OpenClaw should expand through fewer, better-grouped, better-labeled plugin primitives, not through more flat top-level verbs and more mechanically published SDK surface.
Vote matrix · Quick signals
Still need to ship something?
×6Another batch ranked right after the header list — different links, same matching logic.
TRENDING
- Feature Request: Configurable per-minute rate limiting (RPM) for models to prevent 429 errors
- Android: Hermes App + Termux install share ~/.hermes and cause silent permission loops
- hermes update emits unicode-animations ANSI demo in non-interactive logs
- hermes update downgrades aiohttp from 3.13.4 to 3.13.3
- npm install warns about deprecated @babel/plugin-proposal-private-methods
- DingTalk inbound media URLs are skipped as unreadable native image paths
- fix(dashboard): ChatPage clears header action buttons on ALL pages, not just Sessions
- [Bug]: check_web_api_key() hardcodes built-in backends — third-party web search plugins silently disabled
- Hermes Web UI 修复经验:GatewayManager 补丁、进程 D 状态、数据库升级问题
- Telegram gateway can silently drop turn after /stop with response=0 chars while internal work continues
- Bug Report: v0.14.0 上下文污染 — 历史回复碎片回注到新请求
- Bug: hermes skills search table truncates Identifier column — install fails with copied value
- [skills-index-watchdog] Skills index is stale or degraded (degraded)
- Discord approval embed not rendering on web/mobile — embed data present in API but invisible
- Idea: Discord voice-channel participation / opt-in auto-join mode
- [Feature]: Claude Code--ultrawork
- build-arm64 job deterministically fails on cold cache (Azure SAS token expires mid-build)
- [Enhancement] computer_use: action=type should fall back to key events for terminal emulators (Ghostty/Terminal.app/iTerm2)
- Feature Request: Session Recovery on Temporary Provider Outage
- [Bug]: Hermes dashboard not working on NixOS (container)
- [Feature]: Add option to ignore @all/@everyone mentions in Feishu group chats
- QQ Bot WebSocket 频繁断开:长时间工具执行阻塞 asyncio 事件循环导致心跳超时
- patch tool: new_string escape sequences (\t) get written literally
- Feature Request: i18n / 多语言支持(国际化)
- Bug: web_crawl schema lets models auto-guess "instructions" instead of asking the user via clarify
- feat: `!command` prefix for direct shell execution (like Claude Code)
- Expose currently-running cron jobs via /api/jobs (or new endpoint)
- [Bug]: Kanban parent-child handoff: scratch workspace GC destroys artifacts before child can read them
- [Bug, Windows] hermes gateway restart loses session context — planned_stop_marker not written before SIGTERM
- [Bug]: Codex→DeepSeek fallback sends assistant turns without reasoning_content → HTTP 400 (require-side cross-provider failover)
- [Bug]: Update got stuck half way, reboot it, then ModuleNotFoundError: No module named 'hermes_cli'
- Kanban dispatcher corrupt-board handling and multi-profile gateway ownership ambiguity
- Gateway can resend a short fallback message when the real final Telegram response was already delivered
- [BUG] Bedrock: Fix 'Invalid API Key format' for presigned URL tokens
- Secret redaction corrupts code syntax in tool output (write_file, execute_code, terminal)
- Unable to connect Ollama Cloud with Pro Subscription to Hermes
- feat: fuzzy substring matching for /skill autocomplete
- PRD: Autonomous market-impact prediction briefing system
- Kanban dashboard should support task/card deep links
- [Feature] Native Feishu CardKit Streaming: consolidate best-in-class implementations
- [Feature]: Inject mental model into context when using Hindsight
- Interactive CLI hides tool output despite display.tool_progress=all, and hermes chat -v does not restore it
- fix(api_server): _handle_responses drops text.format JSON schema — structured output constraints silently ignored
- state.db FTS corruption goes undetected — no integrity check, no repair path
- bug: fallback routing can select text-only models for image requests and hide the primary failure
- feat(kanban): persist worker session_id per run and pass --resume on respawn after unblock
- feat(kanban): support GitHub/OMO lifecycle bridge for Xiyou-style automation
- Expose update-safe TUI/composer hooks for voice transcript and composer events
- Hide or configure voice transcript status rows in editable dictation mode
- [Feature]: Per-Tool / Per-Toolset Approval Policies
- Context compression creates orphan sessions missing from state.db
- messaging platform
- feat: Add read-only / silent monitoring mode for WhatsApp adapter
- double-.hermes path mismatch, the HOME env var leak, and the fallback-notification UX problem
- Bug: Plattform-Bundle name `hermes-yuanbao` in `agent.disabled_toolsets` silently kills ALL tools in gateway path (Telegram + cron), CLI unaffected
- CLI /yolo (in-chat) does not bypass dangerous command approvals — env var freeze + missing enable_session_yolo call
- OpenAI Codex provider crashes with "'NoneType' object is not iterable" (HTTP None)
- DEEPSEEK_API_KEY blocked by env blocklist in gateway process — cron jobs fail with deepseek provider
- fix(feishu): Card action callback routing issues - invalid message_id and unrecognized /card command
- Discord plugin: profiles without explicit `discord:` block silently get `require_mention=true` + `auto_thread=true` (regression in cc8e5ec2a)
- [Bug]: DISCORD_ALLOWED_ROLES ignored by gateway _is_user_authorized — role-authorized users get 'Unauthorized user' rejection
- [Bug]: /new, /clear, and /reset commands freeze the terminal session
- openai-codex subscription backend returns HTTP 200 with response.output=None, causing Slack/cron failures
- RFC: Centralized Model/Provider Registry
- bug: openai-codex provider — TypeError: 'NoneType' object is not iterable on every request (gpt-5.5)
- [Feature]: Source-aware instruction gate — architectural mitigation for indirect prompt injection
- Named custom provider stale_timeout_seconds ignored because runtime provider is normalized to `custom`
- guard test (ignore)
- [Feature]: per-platform LLM request_overrides (extra_body / reasoning_effort / service_tier)
- One-shot smoke: add Flue-backed orchestration fixture
- Gateway should not treat stale Codex app-server progress as final response after post-tool silence
- `docker_run_as_host_user: true` breaks bundled skills: Hermes home is mounted into `/root/.hermes` but the container runs as a non-root user (`HOME=/home/pn`)
- [Bug]: gateway api_server streaming bypasses server-side tool-call loop when chat_template_kwargs.enable_thinking=false (model emits tool name as plain text)
- [Feature]: Pre-install python-telegram-bot in Umbrel Hermes Docker image
- YouTube Shorts filter not working in youtube-content skill
- v0.15.0 PyPI release breaks ALL platforms — plugin.yaml manifests missing from package
- RFC: On-demand tool/skill/MCP discovery — decouple schema registration from process lifecycle
- Pixshelf: local-first stock photo workflow command center
- [Bug]: baoyu infographic skill should not silently bypass image_generate
- Pixshelf v1.5: manual submission tracking for stock agencies
- `hermes config set` silently accepts unknown keys, writing them where the runtime never reads
- Honcho memory prefetch hang on fresh CLI subprocess in v0.15.0 (regression from #27190)
- [Bug] v0.15.0 Docker image: stage2-hook.sh, main-wrapper.sh missing; container_boot module removed
- Feature: Reduce cache-read token overhead for DeepSeek providers — configurable cache_ttl, skills snapshot trimming, memory compaction
- Windows: three bugs from daily use (plugin discovery, gateway exit code, Unicode decode
- holographic memory: HRR silently degrades to FTS5 when numpy is missing
- Make max_tokens configurable for aux vision calls
- Conversation compression desynchronizes session ID between agent context and gateway routing, causing silent message loss
- [Bug]: v0.15.0 Docker image:The TUI cannot be used in the dashboard.
- cron: skip_memory=True blocks fact_store/memory tools from all cron jobs
- TUI: Node.js OOM crash when agent uses browser tools repeatedly
- feat: model_profiles — per-model toolset and memory config
- Automatic background skill patching disrupts active sessions (severe impact on local models)
- ensure_hermes_home() creates root-owned dirs in profile subdirectories when kanban workers are dispatched
- Feature: opt-in webhook bypass for DISCORD_ALLOW_BOTS — allow operator-initiated probes without weakening bot-loop guard
- v0.15.0: Codex requests fail HTTP 400 when participant display_name contains non-ASCII (emoji breaks input[].name pattern)
- Architecture: State Persistence Precedence (Memory vs Skills vs Hooks)
- [Bug]: cronjob tool: create action always fails with "schedule is required for create" even when parameters are provided
- codex-oauth: 'NoneType' object is not iterable in _run_codex_stream (gpt-5.5) — every turn fails non-retryably
- Docs/Config: Plugin local scope enablement ambiguity
- [Bug]: CLI freezes after using /new command (WSL)
- Profile Codex auth can ignore global credential pool when local state is stale
- [workflow-engine] CRITICAL: variable substitution crashes on regex metachars in user input
- [workflow-engine] HIGH: loop and bash nodes leak subprocesses on timeout
- [workflow-engine] HIGH: README documents config env vars the engine never reads
- [workflow-engine] MEDIUM: workflow_run rate limit bypassable via concurrent calls (TOCTOU)
- [workflow-engine] chore: manifest gaps, side-effectful register(), dead code, unauth kanban dispatch
- [mcp_lazy] HIGH: synthetic mcp_server_<name> stub collides with a real MCP server named 'server'
- [mcp_lazy] HIGH: promote_server eager flag documented but never persisted
- [mcp_lazy] MEDIUM: _prev_mode dict leaks and goes stale; not cleared on session evict
- [mcp_lazy] MEDIUM: get_pool has unlocked check-then-set race on pool creation
- [mcp_lazy] MEDIUM: pre_tool_call gives no guidance for unpromoted server-stub calls
- [mcp_lazy] chore: undeclared pre_tool_call hook, nonexistent 'mcp_load_tools' name in docs, missing tests
- [a2a_fleet] CRITICAL: server never auto-starts — register() runs outside an event loop
- [a2a_fleet] CRITICAL: auth_required defaults to false on a cross-machine surface
- [a2a_fleet] HIGH: remove invented disable() hook — loader never calls it, port leaks on reload
- [a2a_fleet] HIGH: plugin.yaml missing kind / provides_tools / requires_env (token env undeclared)
- [a2a_fleet] MEDIUM: tighten wide-open CORS, anonymous /health peer leak, and peer-URL SSRF
- [a2a_fleet] MEDIUM: relocate tests to tests/plugins/ and cover sync-register + auth-default paths
- xai-oauth auxiliary client incorrectly uses Responses API (CodexAuxiliaryClient), causing 403 on compression/vision/web_extract
- [Bug]: Direct Copilot gpt-5.5 large resumes are killed by 12s Codex TTFB watchdog
- [Bug]: `hermes uninstall` does not work on Windows
- TUI: Thinking block leaks raw JSON and Σ character
- Hostinger VPS: migration Hermes Agent → Hermes WebUI impossible (tini + UID mismatch + sessions)
- /goal judge over-continues exploratory goals unless the assistant explicitly says the goal is complete
- /goal auto-continuation can be amplified by preflight compression/session split and resurrect stale task state
- Dashboard infinite reload loop in loopback mode — GET /api/auth/me returns 401 on every page load
- [Bug]: Provider/LLM switch leaves stale encrypted_content causing 400 errors on Telegram sessions
- [Bug]: Infinite reload loop / React state loop on Sessions tab (Firefox + Chrome) — repeated 401 on /api/auth/me (v0.15.0)
- show_reasoning should work independently of streaming in CLI mode
- Feature Request: Strip reasoning/<think> blocks from TTS preprocessing
- mcp add / mcp test raise NameError when mcp package not installed
- v0.14.0 dashboard breaks behind reverse proxies — two regressions
- Skills hub creates empty category directories when no skills installed
- [Bug]: Custom endpoint: ChatCompletions returns content, but Hermes treats response as empty (v0.14.0)
- fix: atomic_replace() fails with EXDEV when HERMES_HOME is a cross-filesystem symlink
- fix(gateway): Feishu session cancellation orphans session guard, permanently blocking messages
- Custom endpoint pricing can overestimate Crof qwen3.5-9b cost by 1,000,000x
- MCP OAuth callback: module-level port global causes port collisions and structural weaknesses vs upstream
- Bug: send_message tool bypasses validate_media_delivery_path security check
- Proposal: Add Mnemosyne to official memory provider documentation
- feat(swarm): support custom verifier/synthesizer body + skills
- Template conversion failed
- Error occurred in the operation of the agent node in the workflow.
- PubSub client overrides Sentinel client when REDIS_USE_SENTINEL is enabled
- Frontend description of the Retrieval node output does not match the actual output
- JSON type input var raise Intenal server error
- cannot extract elements from a scalar
- 负载均衡 为模型配置多组凭据,并自动调用,此功能无法选择
- add models is error
- panic: could not create filter
- Persist partially generated messages when /chat-messages/:task_id/stop is called
- MCP server connection fails with 403 — request never leaves Dify (SSRF proxy suspected)
- Support durable async execution backends for long-running workflow steps
- [Xiaomi MiMo] Credentials validation fails with 400 "Not supported model mimo-v2-flash" when using Token Plan endpoint (v0.0.7)
- After clicking preview on a parent-child segmented knowledge base, it shows 0 chunks
- Retrieval score differs between UI upload (.docx) and API upload (.txt) despite identical chunk content and embedding model
- gemini cli crash again
- Xbox gift card code damage
- Damage caused by the gemini cli crash
- ioctl(2) failed, EBADF (Bad File Descriptor)
- Feat: Support Bun as an alternative runtime/package manager for updates and extensions
- fatal error again!!!!
- ioctl error
- Critical Crash: ioctl(2) failed, EBADF in ShellExecutionService.resizePty
- ioctl(2) failed, EBADF
- v0.44.0 Regression: Critical crash with ioctl(2) failed, EBADF during PTY resize
- Crash on startup: ioctl(2) failed, EBADF in UnixTerminal.resize
- Crash: `ioctl(2) failed, EBADF` in `node-pty` during PTY resize on macOS
- Gemini CLI crashes with `ioctl(2) failed, EBADF` in `node-pty` during `resizePty`
- Remote Role
- ERROR ioctl(2) failed, EBADF /home/mich
- RangeError: Maximum call stack size exceeded
- EBADF Error during folder creationg broke session and terminal glitches
- MAIP / Gargoub Project - Mediterania - North Coast
- Gemini cli crash again in this morning
- ERROR ioctl(2) failed, EBADF
- Verified node install fails — Checksum verification failed (Cloud)
- The extended debugging key did not arrive during registration.
- CollaborationPane unmounts collaboration store on single-user instances, causing permanent "No network connection" state
- Workflow cannot be saved when the name contains "->" (Potentially malicious string)
- automation does not work and does not show an error
- Raj Ai Automation
- Default Data Loader: DOMMatrix is not defined error
- Feature: Per-node execution timestamp overlay on canvas during workflow run
- AI Agent + Vertex `gemini-3.5-flash`: 400 "missing thought_signature" on sequential multi-turn tool calls (post-#24982)
- PDF Loader in Pinecone Vector Store fails due to pdf-parse version conflict (v2 not supported)
- emailReadImap: add UID deduplication, batch size cap, and numeric uid enforcement
- Manual node execution fails with "Could not find a node" when autosave is disabled (N8N_WORKFLOWS_AUTOSAVE_DISABLED)
- Schedule Trigger stopped firing — workflow Published & active, manual executions succeed, no automated fires for 2+ hours
- [MCP SDK] create_workflow_from_code intermittently returns HTTP 500, often as a false negative (workflow persists anyway, causing duplicates on retry)
- Credential-load wedge: workflows using googleApi/jwtAuth credentials silently fail to execute after key rotation
- Google Sheets Trigger every minute is not working manual Execute is working sent email
- [BUG] Plugin marketplace MCP connector remains stuck "still connecting" when mcp-remote requires OAuth
- [redacted at user request]
- Opus 4.7 behavioral regression: loaded instruction-following discipline degraded in recent Claude Code/Cowork updates
- [BUG] Tailscale via Homebrew CLI + Mac App Store GUI, both Macs on macOS, Cowork blocked by VPN detector despite Tailscale being a mesh VPN with no traffic interception
- stopShellPty on tab switch kills active sessions (exit 143) — regression in May 27 build
- [BUG] Long URLs are broken into multiple lines and become unclickable in terminal output
- [BUG] claude rm/stop/reap SIGKILLs background session tree without SIGTERM grace, orphaning git index.lock and similar
- [BUG] Default git workflow in the system prompt was pushed without context or consent
- [MODEL] Inconsistent output quality / Ignoring instructions (overfitting and inappropriate repetition of Korean vocabulary)
- You've hit your weekly limit · resets May 31 at 5pm (Asia/Shanghai)
- Paid yearly subscription silently downgraded to Free with no user action
- [Regression v2.1.153] Plugin bash hooks fail with "echo: write error: Permission denied" on Windows (claude-mem, shell: "bash")
- [BUG] Connector toggles in conversation are not clickable — must click text label instead
- [remote-control] Input from mobile app/browser not reaching host session — output works fine
- Model fails to read/reference CLAUDE.md contents despite being loaded in context
- [BUG] Claude Desktop reinstall destroys Code chat history (transcripts + Recents) while regular Chat history, project files, and memory all survive
- Bypass mode clamps to Accept Edits even with the toggle ON (Claude Code Desktop 1.9255.2 / CC 2.1.149)
- [BUG] TUI input freezes randomly mid-typing — entire prompt becomes unresponsive for minutes
- [BUG] Cowork downloads Linux ELF binary instead of macOS binary on macOS Sonoma 14.8.7 — exit code 132 (SIGILL) on every session
- [Feature Request] Persistent project memory — sessions forget everything on close, forcing users to keep many sessions open
- [Bug] Thread context stale after sleep/resume, returns outdated date and calendar data
- [FEATURE] Add context window usage indicator and warning before auto-compaction
- [BUG] Dictation error: Invalid character in header content ["x-config-keyterms"] on Windows
- [Bug] Anthropic API Error: Server rate limiting despite normal usage
- Does delegating work to `claude -p` subprocesses reduce context accumulation in the parent session?
- [BUG] Claude Code hangs on M1 Mac when terminal says "opening browser to sign in" and browser opens
- [BUG] Claude_Preview MCP preview_start spawns dev server with main-repo cwd instead of session's worktree cwd
- [Bug] Anthropic API Error: Server rate limiting during request execution
- [Bug] Anthropic API Error: Server rate limiting on concurrent requests
- [Bug] Ultraplan ready notification fires before cloud agent completes execution
- [BUG] API 500 ERROR ALL THROUGHOUT THE DAY
- [BUG] Cowork: Live Artifacts folder path changed in 1.9255.2, no automatic migration from Documents\Claude\Artifacts
- [Bug] Auto-compact never triggers despite statusline reporting "100% context used" (v2.1.153, Max sub, 200K mode)
- [BUG] [Desktop / macOS] 'Open in → New Window' detached session: font renders smaller than main, no per-window controls, Cmd+/Cmd- keystrokes routed to main window instead
- Feature request: option to switch between classic and new minimal UI
- [Feature Request] Show timestamps for each message
- [BUG] Terminal corruption when permission prompt appears while navigating Agent Teams agent selection menu
- [FEATURE] Allow users to customize the background color of the Claude desktop app beyond the current light/dark theme presets.
- [BUG] Statusline not displaying on Windows [fixed]
- Background agent UI Stop button is a no-op for stuck agents — process keeps consuming tokens
- Background agents silently die on session pause/resume — no completion notification, no work recovery
- Add option to hide email address from welcome banner
- [BUG] SSH Remote: `projects` field in remote ~/.claude.json becomes null after desktop restart — jsonl files intact, UI shows 'No messages yet' for every session
- [Bug] Claude Code not applying fixes despite claiming to complete tasks
- billing is unfair and poorly documented
- [BUG] Claude Code on the web: declared plugins inactive on first session, require restart to fully load
- [BUG] Restore from archive deleted sessions instead of restoring them
- [BUG] M365 connector fails with AADSTS50011 in Cowork — localhost vs 127.0.0.1 redirect URI mismatch
- claude agents: workflow slash-commands missing from dispatch-input completion (regression-adjacent to #61424)
- Claude Desktop's Info.plist missing TCC usage strings, blocks all EventKit-based MCP servers
- False-positive safety blocks on self-administered governance amendments — request for owner-authority mode for verified professional users
- [BUG] Stop pushing "AUTO"-mode
- [DOCS] Plugin marketplace guide omits `skipLfs` option for git-based sources
- [DOCS] MCP docs omit combined startup notification for MCP server and connector authentication
- [DOCS] Agent view docs omit macOS Privacy & Security identity for background agents
- [DOCS] Npm update docs do not explain release-channel behavior for `claude update`
- [DOCS] Agent SDK docs omit `subagent_type: "claude"` worktree and output persistence behavior
- [DOCS] Background session docs omit `$CLAUDE_JOB_DIR` temp-file behavior
- [FR] mask env-var values in 'claude mcp get <server>' output
- [FR] subagent worktrees should not inherit stale local 'user.email' from prior dispatches
- [BUG] Windows: Grep tool leaks rg.exe + conhost.exe processes (~2000 zombies / 14 GB RAM in long sessions)
- [BUG] Stats dashboard "Peak hour" appears off by one hour
- [BUG] Diff highlight (teal SGR background) bleeds past changed text in 2.1.150–2.1.153
- [FEATURE] confirm before deleting session
- Plugin PostToolUse hooks still silently skip in Claude Desktop / Cowork (re-filing closed #51904)
- /code-review skill: silent fallback to main...HEAD reviews other people's commits, and JSON-only output is hard to read
- Monitor tool doesn't source the shell snapshot like Bash does; PATH-dependent tools (jq, sleep, etc.) fail in Monitor commands on macOS/Nix
- [Bug] Long input lines truncated with ellipsis while typing instead of wrapping in terminal UI
- [FEATURE] VS Code extension: Render submitted user messages as Markdown in chat
- OSC 52 copy from Claude TUI doesn't reach clipboard inside tmux (regression in 2.1.146–2.1.153)
- [BUG] RemoteTrigger create/update returns HTTP 400 with circular error: "event_type is required" / "unknown field event_type"
- [BUG] Option to hide or minimize the built-in "status footer" (multi-line debug/cost panel) [re-raise of #31475]
- [Bug] Feedback submissions being closed without review or action
- [FEATURE] Word-jump cursor navigation in Chat input (option+arrow / bindable actions)
- [FEATURE] ! shell mode: filesystem tab completion
- [BUG] API Error: Usage credits required for 1M context
- claude agents: OSC 52 clipboard emission broken in tmux (regression in 2.1.146–2.1.153)
- CLI crashes on macOS 15 M3 - exit code 1
- [FEATURE] Support Cmd+V image paste from clipboard
- [FEATURE] Enhance claude.ai M365 connector to support MS Planner
- [BUG] Slash command autocomplete hijacks pasted absolute file paths starting with /
- PreToolUse hook `if` filter false-positives on complex Bash commands
- [BUG] Diff panel hangs/whites out
- Feature Request: Support drag-and-drop for binary documents (.wps, .doc, .docx, .xlsx, .pdf) in VS Code extension
- [BUG] activation of 1M context in VSCode
- [FEATURE] Support i18n / language localization for built-in slash command outputs
- Ctrl+V para colar imagens deixou de funcionar no CLI (Windows, PowerShell)
- [FEATURE] Please add Norwegian (Bokmål/Nynorsk) language support to the Claude Code interface
- [BUG] OTel log events (claude_code.user_prompt, api_request_body, tool_decision, hook_execution_complete) emitted with empty trace_id/span_id while sibling spans correlate correctly
- [BUG] Cowork crashes on every message, no VM logs generated, missing AppData\Roaming\Claude
- [FEATURE] first-class session handoff + per-session token budgets for unattended runs
- [FEATURE] Smart paste: convert clipboard code to file reference chips (like Cursor)
- [Feature Request] Restore chat pin functionality to title chat submenu
- [BUG] SIGILL issues with version 2.1.153
- [BUG] Cowork plugin upload fails with generic "Plugin validation failed" when a `description` field in any SKILL.md frontmatter contains angle brackets (`<…>`)
- [BUG] Desktop App 2.1.144+: startup scanner deletes cliSessionId from claude-code-sessions local files on every launch — session not found on disk
- [Feature Request] Add keyboard shortcut to copy last message with proper formatting
- [MODEL] Opus 4.7 not 1M
- Allow naming/renaming background agents in `claude agents` view
- Stale worktrees in .claude/worktrees/ are never cleaned up, consuming massive disk space
- Agent worktrees are never cleaned up, silently consuming disk space
- Subagent worktrees not auto-cleaned when reviewer writes scratch files
- [Bug] Skill initialization hangs for extended duration in Plan Mode
- Claude Desktop writes malformed registry Run entry (nested escaped quotes) - crashes Windows Task Manager and other Run-key parsers
- IME candidate window shows at bottom-right corner instead of caret position (Windows CMD)
- [BUG] Pressing 'Escape' doesn't close the /BTW conversation when the main conversation is asking for approval
- [BUG] Opus 4.7 (1M) intermittently emits empty-string values for tool_use.input fields, killing the session
- FleetView agent UI shows "running" with incrementing elapsed time after agent has returned
- /doctor flags context-scoped cmd+c binding as macOS conflict (false positive)
- [BUG] Text Rendering in Elvish
- Desktop app: Bypass Permissions mode flips to Accept Edits on first prompt (M5 / macOS 26.5)
- [Workaround] Date-Weekday Verification Hook — Prevents Claude from writing wrong weekdays
- [BUG] Claude Code create c:/memfs directory without asking me.
- [BUG] Claude Code's Bash execution waits forever with no processes running
- [BUG] usage stays stuck waiting for 5 hr limit after upgrading to premium seat in team plan
- [Workflow tool] resume cache is unreachable for nontrivial workflows because LLM dispatchers can't transcribe args byte-exactly
- Code review (Preview): "Add a repository" shows no results for private GitHub org repos
- [BUG] /context commands blows up context
- [Feature Request] Add precache expiry hook to enable proactive compaction before token eviction
- [BUG] Context indicator shows 0% at session start despite ~20K+ tokens already loaded
- [Feature Request] Add semantic search for --resume session history
- [Feature Request] Add session search, tagging, and filtering capabilities
- [BUG] Cowork Dispatch reports "desktop not available" on Windows 11 while standard Cowork works normally
- [Bug] Claude Code provides incorrect suggestions with high confidence despite errors
- defaultMode: acceptEdits silently overrides per-path permissions.ask rules for Write/Edit
- [FEATUR configurable tip interval (e.g. tipIntervalSeconds: 30 in settings)E]
- Plugin marketplace fails to load: schema rejects 'displayName' key (v2.1.153)
- claude agents: in-session copy uses broken OSC 52 path while overview correctly uses tmux buffer
- [BUG] Plugin agent descriptions (and custom agents) load unconditionally into context — no parity with disable-model-invocation for skills
- Crashed ultrareview consumed a free credit despite producing zero findings
- [Bug] Character rendering issue - invisible or missing text display
- [BUG] Cowork: processo Claude Code encerra com código 3 — .claude.json não contém token de autenticação (Windows 11 25H2)
- [BUG] 2.1.153 silently discards tools/list response from rmcp 0.12.0 HTTP MCP server (works in 2.1.152, wire-identical handshake)
- VS Code extension: option to auto-resume last session when reopening a workspace folder
- [Bug] Conversation continuation failure
- [BUG] Cowork crashes every time I start a new chat or attempt to continue an existing one in any project. The error displayed is: "Claude Code è andato in crash
- [Bug] Unannounced quota changes
- Native update/install fails with 'socket connection was closed unexpectedly' behind proxy — undici TLS incompatibility
- [BUG] Session name reverting after manual change
- [BUG] 非正常思考,上下文过长时,一直显示思考,点击interrupt按钮失效
- Honor `tools:` frontmatter when an agent is invoked via `@mention` — strip `Task` only when the agent did not declare it
- macOS TCC popup still recurring on v2.1.153 — "2.1.153" would like to access data from other apps
- Claude Code leaks pty handles — exhausts pseudo-terminals on macOS after long session
- [Bug] Agent fails to execute or respond to user input
- [BUG] Persistent "Expecting value: line 1 column 1 (char 0)" JSON parse error after tool execution
- [Feature Request] Implement proactive unit test coverage recommendations for recurring bugs
- VS Code panel lacks status line + terminal lacks image paste in Codespaces, forcing a tradeoff
- `/powerup` only shows ~10 lessons — allow viewing the full catalog
- [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin
- [Bug] VSCode terminal output displays corrupted text with garbled symbols
- [Feature Request] Add LaTeX/KaTeX math rendering to TUI
- [Bug] Sub-agent PR review results not validated by orchestrating agent
- Subagents on Pro 1M tier: trivial probes pass, real workloads fail at first tool call (probe-vs-workload divergence)
- Path-scoped rules and subdirectory CLAUDE.md not loaded when creating new files matching the pattern
- AskUserQuestion: cancelling during extended thinking poisons the whole session with 400 'thinking blocks cannot be modified' (2.1.153); concurrent prompts overwrite each other
- Ideas Missing from Claude Cowork Menu (Windows)
- [BUG_BOUNTY_SAFE_POC_2026] Prompt Injection RCE Test - Command Execution Proof
- [BUG] Cowork scheduled task: execution history row not showing after successful run
- Resuming an extended-thinking session fails permanently with 400 "thinking blocks cannot be modified" (transcript stores thinking text as empty but keeps signature)
- [Bug] Plugin-registered CwdChanged and FileChanged hooks don't fire (settings.json works) — v2.1.153
- Auto-archive on PR merge / branch delete — clarify autoArchiveSessions semantics or add dedicated opt-out
- `claude mcp add` echoes Authorization header value verbatim to stdout, leaks bearer tokens to terminal and session transcripts
- [BUG] Bug report — /insights skill, Claude Code The /insights skill outputs a malformed file path.
- Plugin slash commands render with '*'-inline format instead of two-column, despite matching official plugin shape
- [Bug] Unexpected long text generation without user input or goal
- [Bug] Thinking blocks causing task progression blocked without user modification
- [BUG] (Critical!) contamination by an unknown session simirlar to the report => [Bug] Context contamination after auto-compact with unrelated email draft of Tejo/Sado Basin #63137
- [Critical] Opus 4.7 Korean output degeneration — Korean grammar itself collapses in long contexts
- [BUG] Title: Autocompact buffer persists across /clear — wastes tokens for irrelevant old context
- [Bug] Auto-Compact loses user input before processing in conversation history
- Feature: per-invocation effort parameter + runtime session-config introspection for skills
- Auto-mode classifier mislabels Azure DevOps vote -5 as "Reject" when denying PR vote actions
- [BUG] Claude Desktop and Claude Code CLI never re-register MCP tools after OAuth 2.1 handshake on a remote HTTP server
- [BUG] Workspace file tags leak across sessions
- [BUG] Ink renderer crashes on Windows 11 build 26200 (Canary) duplicate banners, terminal mode leaks, mid-operation aborts
- [BUG] Claude Code Desktop issue
- PTY master fd leak in Claude desktop app exhausts macOS kern.tty.ptmx_max after ~2-3 days
- [BUG] Claude Code — Session Management after Unexpected Interruption
- [Windows] Cowork OpenTelemetry exporter does not initialize - zero events emitted to any destination, including loopback
- [Bug] Opus 4.7: 400 `thinking blocks ... cannot be modified` on long extended-thinking sessions, triggered by history-altering events (scheduled prompts / parallel tool-call cancellation)
- [BUG] API Error: Server is temporarily limiting requests (not your usage limit) · Rate limited
- Multi-plugin custom marketplace: only first plugin registered in installed_plugins.json, skills don't load
- [BUG] Git push through the SDK's git proxy fan-outs into ~500 GitHub REST API calls, exhausting the 5,000/hour budget after a handful of pushes
- [BUG] Claude took liberties it really shouldn't with my global config
- [BUG] Agent window focus lost after navigating with arrow keys, causing scroll deadlock
- [BUG] `--model` flag silently ignored in interactive sessions (works in `--print` only)
- [BUG] Dispatch permanently shows "desktop appears offline" on Windows 11 - never worked on first use
- feat: support per-command enableWeakerNetworkIsolation as safer alternative to dangerouslyDisableSandbox
- /code-review outputs a raw JSON array instead of readable findings
- [BUG] Cowork — Additional allowed domains ignored on Team plan; same domain works on Pro plan
- Haiku
- [Bug] False positive blocking beneficial outcomes in tool execution
- 3P Bedrock SSO: credentials silently expire without triggering re-auth on day 2+
- CLAUDE_AUTOCOMPACT_PCT_OVERRIDE in settings.json env block silently ignored by autocompact logic
- Auto-compaction deletes main session JSONL before verifying summary completion, causing data loss
- [Bug] Claude Code not executing stated actions or producing expected results
- [FEATURE] Deferred Messages — Queue Input for End of Turn
- [BUG] Up/Down arrows in input box navigate history instead of moving cursor — regression in 2.1.149+
- Cancelling a parallel tool-call batch corrupts thinking blocks -> 400 "thinking blocks cannot be modified" permanently wedges the session
- Claude Code caused data loss, then contradicted itself about recovery (two incidents, one session)
- [Bug] Unclear error messages from Claude Code CLI
- [Bug] Agent tool rejecting due to context size limit exceeded
- claude agents: daemon and bg-spare processes spin at ~100% CPU when idle
- [BUG] Compaction fails with "context window limit" error even when context usage is low (e.g., 20%) — regression in v2.1.153
- Remote Control entitlement lost after May 27-28 incident — `Error: Remote Control is not yet enabled for your account` on active Max subscription
- PreToolUse hook exit code 2 does not block Write tool
- [Bug] Thinking blocks in latest assistant message are immutable
- GUI: dispatch file:// and custom-scheme clicks to OS shell handler
- Show current model in statusLine by default
- [Bug] Agent console becomes unresponsive to keyboard input after multiple agents initialized
- [FEATURE] PreToolUse hooks should have a way of updating the environment
- [Bug] Unable to start or use Claude Code CLI
- [BUG] Repository not visible in Claude Code web repo picker
- Session permanently wedged on 400 "thinking blocks cannot be modified" after parallel tool_results
- [Bug] @ autocomplete loses sibling repos after a file edit in multi-repo workspace
- Unclear error message when creating sub-agent without authentication
- [Bug] Anthropic API errors causing frequent failures and high token usage
- [BUG] @ mention file picker only shows packages, not individual files (desktop app - Code tab)
- [Bug] TUI panel footer remains sticky and consumes excessive terminal space
- PR-status polling exhausts GitHub GraphQL rate limit on repos with many open PRs
- [BUG] Windows: welcome panel not shown in some project folders (2.1.153)
- [Bug] Anthropic API Error: thinking blocks corrupted during context compaction with extended thinking enabled
- API 400 "thinking blocks cannot be modified" permanently bricks session during agent activation (interleaved thinking + tool use)
- Right-click Copy copies the whole message instead of the selection; pasted text retains dark background
- Mid-session model switch corrupts conversation when extended thinking is enabled (API 400: 'thinking blocks cannot be modified')
- [BUG] Markdown file links in chat output do not open files when clicked (VS Code extension)
- Stuck retry loop: `400 thinking blocks cannot be modified` on large interleaved-thinking turns using AskUserQuestion
- [FEATURE] Prompt user for approval before auto-compaction proceeds
- Custom MCP connectors not attachable to scheduled routines — no UUID discovery path
- [BUG] Claude in Chrome — Navigation blocked for teams.cloud.microsoft and outlook.cloud.microsoft after Microsoft domain migration**
- [BUG] Claude Desktop — Personal plugins panel renders list but is entirely non-interactive (macOS, v1.9255.2)
- [Bug] error when using Workflows
- [BUG] Persistent "update available" notification despite being on latest version
- [BUG] Sweep Agent from /code-review never completes
- [Bug] Tool calls not executing or returning results
- [FEATURE] Cloud-synced memory and settings across machines
- [Bug] Terminal UI freezes when Ctrl+O view exits during interactive prompt in plan mode
- Continuous api errors when using claude code with Opus 4.7 with thinking on low
- [Feature Request] Add support for installing and using previous Claude Code versions
- [Bug] Extended Thinking: Summarized thinking blocks fail signature validation when resent to API
- [Bug] Anthropic API Error: 'thinking' blocks cannot be modified
- [Bug] Anthropic API Error: Thinking blocks cannot be modified with extended thinking mode
- Feature request: Lazy/on-demand MCP server connections
- [Bug] Tool Arguments Parsed as String Instead of Object
- [Bug] Anthropic API Error: Insufficient context provided
- [Bug] Claude Opus occasionally uses moskovian(russian) orthography instead of Ukrainian in system-prompted responses
- Opus 4.8: backgrounded task completions (subagents AND Bash) crash with 400 "thinking blocks cannot be modified"
- [Bug] Opus 4.7 fabricates stable preferences ("my default") to rationalize arbitrary choices when challenged
- [Bug] Unable to update Claude Code CLI
- [BUG] Desktop app: /remote-control mints link + connects bridge (main.log) but in-chat link/QR panel never renders
- Feature: sessionColor and sessionName in .claude/settings.json
- [BUG] Anthropic API error: thinking blocks
- [FEATURE] Support Remote MCPs in Cowork as in Claude Code
- [Bug] Anthropic API Error: 400 Bad Request with Redacted Thinking - 0 4.7 & 4.8
- [Bug] Anthropic API Error: Cannot modify thinking blocks from different model versions
- Interleaved thinking + multi-tool turn corrupts thinking block (text blanked, signature kept) → permanent 400 'blocks must remain as they were'
- [BUG] Mode/permission changes mid-tool-loop (effortLevel: xhigh) poisons entire session
- Session failure log: Opus 4.6 ignores its own rules for an entire session
- [BUG] "400 Guardrail was enabled" error when using Claude Opus 4.8 with AWS Bedrock
- [Feature Request] Add subagent approach selection option to avoid accidental feedback
- Persistent 400 'thinking blocks in the latest assistant message cannot be modified' — interleaved thinking persisted with empty text + signature bricks sessions
- [BUG] DesktopvsApp
- [BUG] Opus 4.7 cache hit rate collapse after May 27 incident — Messages 1.1k→88.9k in 9 minutes, $630/session
- [Bug] Anthropic API Error: Invalid thinking block format
- [BUG] FUCK CLAUDE
- Opus 4.8 extended thinking: Stop hook block re-entry corrupts thinking blocks → 400
- [Bug] 4.8 Fails when accessing previous model history
- [Bug] Unintended File Modifications During Execution
- [DOCS] Model configuration docs omit lean system prompt default scope and model exceptions
- Add "Always allow globally" option to permission prompts
- Server-side model upgrade (Opus 4.7→4.8) wedges in-flight sessions with `thinking blocks cannot be modified` 400
- [DOCS] AskUserQuestion docs missing multiple-choice prompt decision threshold
- [DOCS] Agent view docs omit shell-command background session launch syntax
- [DOCS] Agent view dispatch input docs incorrectly imply `/logout` dispatches as a prompt
- [DOCS] Claude in Chrome docs omit connected-browser selection behavior
- [DOCS] Plugin docs omit `defaultEnabled: false` for opt-in plugins
- Feature Request: Customizable chat text colors for user and assistant messages
- [DOCS] `/plugin` Discover tab docs omit directory-based suggested plugin pins
- VSCode Chrome integration silently fails: 3 distinct bugs
- [DOCS] MCP stdio docs omit session environment variables
- [Bug] Anthropic API error on second request within session with Claude Opus 4.8
- Cowork emits a blank session "index" handoff on focus when a CLI session is paused awaiting input
- [DOCS] MCP docs omit `claude mcp list/get` pending-approval output for unapproved project servers
- [BUG] /compact fails with 400 error when last assistant turn contains thinking blocks
- [DOCS] `/claude-api` docs omit Opus 4.8 migration guidance
- [DOCS] Fast mode docs still recommend deprecated Opus 4.6 override variable
- [DOCS] Bash tool docs omit `$TMPDIR` consistency across sandboxed and unsandboxed commands
- [Bug] Anthropic API Error: 400 Bad Request on Extended Thinking
- [DOCS] Background session docs omit worktree-isolation behavior for spawned subagents
- Built-in mechanistic self-verification of verifiable claims (symmetric to the auto permission gate)
- [DOCS] Worktree docs do not clarify `worktree.baseRef: "head"` inside linked worktrees
- [BUG] Excessive RAM usage with multiple parallel chats (~10 sessions → 30 GB memory pressure, macOS OOM)
- [DOCS] Managed MCP policy docs omit invalid `allowedMcpServers`/`deniedMcpServers` entry behavior
- [DOCS] Effort docs omit `CLAUDE_CODE_ALWAYS_ENABLE_EFFORT` unsupported-model behavior
- Regression (2.1.147–2.1.150?): resuming an extended-thinking session after a CC update/model-switch → unrecoverable 400, session bricked
- [DOCS] Windows updater docs omit `claude.exe` in-use recovery guidance
- [DOCS] VS Code auto mode docs still tie mode-picker visibility to bypass-permissions setting
- [DOCS] MCP docs omit `/mcp` tool list and detail rendering behavior
- [DOCS] Fine-grained tool streaming docs still describe provider opt-in behavior
- bypassPermissions: session startup reads flat pref, GUI toggle writes per-account pref — they never sync
- [BUG] Claude Desktop Code tab causes disk write limit violation — 8.5GB in 11 min, macOS kills app (M5, v1.9659.1)
- Ultrareview v2.1.96: docs describe /tasks command + claude ultrareview --json subcommand that don't exist; findings hard to read after completion
- I'd be happy to help create a GitHub issue title, but I don't see the error message in your message. Could you please share the specific error you're encountering? That way I can generate an accurate and descriptive issue title for you.
- [BUG] Claude in Chrome `file_upload` rejects all scheduled-task sessions with misleading error (real cause: INVALID_SESSION)
- Extended thinking: signed thinking block 'cannot be modified' (400) permanently wedges session
- RTL text support for Hebrew (and Arabic) in Claude Code
- [Bug] Random errors occurring across multiple operations