* fix(memory-wiki): strip fenced code and inline code before wikilink extraction
The wikilink extractor in extractWikiLinks previously scanned
the full markdown content including fenced code blocks and
inline code spans. Literal [[...]] syntax inside code regions
(e.g. bash test syntax, Scala generics like
Future[Option[User]]) produced false-positive broken-wikilink
lint warnings.
Add FENCED_CODE_BLOCK_PATTERN and INLINE_CODE_PATTERN and
strip those regions from the searchable text before running
the Obsidian and Markdown link regexes. Five regression tests
cover backtick-fenced, tilde-fenced, 6-backtick-fenced, and
inline code scenarios plus a full vault lint round-trip.
Fixes#97945
* fix(memory-wiki): accept longer closing fence in code block stripping
The FENCED_CODE_BLOCK_PATTERN used a regex backreference (\1)
that required the closing fence to be exactly identical to the
opening fence. CommonMark allows the closing fence to be the
same character type and at least as long — e.g. ``` opening
with ```` closing is valid. Switch to a function-based
replacement that compares fence character type and length.
Add regression test for the longer-closing-fence case per
ClawSweeper review feedback (#98095).
* style(memory-wiki): add braces to if-body in fence replacement callback
Fixes ESLint curly rule violation at line 403.
* fix(memory-wiki): replace fence regex with line scanner for wikilink extraction
Replace FENCED_CODE_BLOCK_RE regex/callback with a line-by-line scanner
that keeps searching past invalid fence-looking lines (e.g. a shorter
``` line inside a longer `````` block) until a valid closing
fence of the same character type and at least as long is found.
Also handles tilde fences and longer closing fences per CommonMark spec.
🦞 diamond lobster: L2 evidence (5 real function-call scenarios, all passing)
Ref. https://github.com/openclaw/openclaw/pull/98095
* fix(memory-wiki): use CommonMark code masking
---------
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat(logbook): automatic work journal plugin with a plugin-contributed Control UI tab
Squash of PR #99930 work for rebase onto the Control UI route refactor:
- extensions/logbook: Dayflow-style capture -> observations -> timeline cards
pipeline with SQLite store, node capture commands, standup/ask, retention
- plugin SDK/gateway seam: surface "tab" Control UI descriptors projected
into hello-ok controlUiTabs (scope-filtered, deterministic order)
- Control UI: dynamic plugin tabs with bundled Logbook view
- docs, tests, labeler wiring
* feat(ui): port plugin tabs and Logbook to the route-owned Control UI architecture
- shared /plugin route carries the tab id in the query (?id=<tab>), matching
the router's exact-path contract
- openclaw-plugin-page renders bundled views (Logbook), sandboxed plugin
frames (descriptor path), or the unavailable card
- sidebar renders hello controlUiTabs after each group's static routes
- Logbook view/controller live under ui/src/pages/plugin/
* fix(ui): namespace plugin tabs by pluginId to prevent cross-plugin tab id collisions
* fix(logbook): prefer app capture nodes and rotate off failing nodes
* fix(plugins): reject protocol-relative Control UI tab paths
* fix(logbook): harden automatic journal
* docs(changelog): remove maintainer self-credit
* chore(ui): refresh locale metadata after rebase
* fix(logbook): preserve analysis window boundaries
* fix(logbook): align status privacy and timezone
* fix(ui): stop hidden plugin tab polling
* refactor: extract reusable AI runtime package
* refactor: complete AI provider relocation
* refactor: keep llm core internal
* refactor(ai): make @openclaw/ai self-contained with host policy ports
Move pure transport helpers (tool projections, strict-schema normalization,
prompt-cache boundary, stream guards, anthropic/openai compat, request
activity) from src into packages/ai; move utf16-slice into
normalization-core. Inject host policy (guarded fetch, redaction,
strict-tool defaults, diagnostics logging) through AiTransportHost with
inert library defaults installed by src/llm/stream.ts. Narrow the public
barrel to instance-scoped createApiRegistry/createLlmRuntime; the
process-default runtime moves behind internal/ and
registerBuiltInApiProviders takes an explicit registry. Delete the
src/llm/api-registry re-export facade.
* fix(ai): teach node, jiti, and vite resolvers the @openclaw/ai and utf16-slice subpaths
The workspace alias tables in root-alias.cjs, plugin-sdk-native-resolver,
sdk-alias, the shared vitest config, and the Control UI vite config only
knew @openclaw/llm-core; Node-side plugin loading resolved @openclaw/ai
through the pnpm symlink to the unbuilt dist (checks-node-compact CI
failures), and the Control UI build broke on the new
normalization-core/utf16-slice subpath.
* chore(ui): drop leftover service-worker debug logging
* build(release): ship @openclaw/ai with its own shrinkwrap and honest dependency set
packages/ai declares only its six real runtime deps (kysely, chalk, json5,
tslog, zod, fs-safe, and proxyline were never imported); orphaned root deps
removed. generate-npm-shrinkwrap now treats publishable packages/* like
publishable plugins so the AI tarball pins its transitive tree even though
workspace deps are omitted from the root shrinkwrap. knip learns the
package entry points; the tsdown dts neverBundle option moves to its
documented deps.dts home; the README documents the no-semver internal/*
contract and host ports.
* docs(ai): add minimal external-consumer example app
examples/ai-chat consumes only the public @openclaw/ai surface (built dist
via the workspace link): isolated runtime, built-in provider registration,
one streamed completion. Supports Anthropic/OpenAI via env keys and a
keyless local Ollama target; live-verified against Ollama.
* docs(ai): document the @openclaw/ai package and workspace shrinkwrap boundary
* chore(check): include examples/ in duplicate-scan targets
* fix: emit normalization package subpaths
* fix: complete AI package boundary artifacts
* fix: align AI package boundary contracts
* fix(ci): stabilize package release contracts
* test: align documentation contract checks
* test: keep cron docs guard aligned
* test: align restored docs contract guards
* test: follow upstream docs contracts
* docs: drop superseded talk wording
Refactor the Control UI around route-owned page lifecycle and state while preserving existing behavior and design.
Prepared head SHA: bd51b6fa76ec057fde19bc31f93fb4aad70416d3
Co-authored-by: Shakker <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
* feat(imessage): add native poll action
Wire the imsg CLI 'poll send' bridge command into the iMessage channel
message-tool action surface, mirroring the existing Discord poll action.
Adds the 'poll' action (gate: polls), a sendPoll runtime, selector-gated
capability advertisement (pollPayloadMessage), config type + zod schema,
regenerated channel metadata, docs, and tests.
* feat(imessage): read inbound polls, vote, and suppress vote echo
Builds on the native poll send action:
- Inbound polls now render to the agent as a readable line (question +
numbered options + tallies) instead of the raw 0xFFFD balloon placeholder,
so a received poll no longer reads as an empty message.
- New `poll-vote` action casts a vote via `imsg poll vote`, resolving a
1-based option index / text / UUID to the poll's option identifier.
- message_tool_only echo guard: the model tends to narrate its choice in a
text reply right after voting ("Blue."), which is redundant since the vote
shows on the poll. A new `poll_vote_echo` suppression reason (alongside
inbound_metadata_echo / internal_runtime_context_echo) drops a send/reply
that exactly restates the just-cast vote, using the option label imsg
returns. Extra content passes through untouched.
* fix(imessage): gate poll-vote on imsg poll.vote rpc capability
Released imsg carries the pollPayloadMessage selector (poll create) but
predates the poll.vote CLI/RPC. Gating both poll and poll-vote on that
selector alone would advertise a vote action the released CLI rejects.
Gate poll-vote additionally on the advertised poll.vote rpc method so this
plugin can ship ahead of the imsg release.
* fix(imessage): enforce poll.vote capability at execution, not just discovery
Codex review flagged the discovery gate as bypassable: a caller that already
knows action=poll-vote skips describeMessageTool and reaches handleAction
directly. Add the same imessageRpcSupportsMethod(status, 'poll.vote') check in
the poll-vote execution path (after assertPrivateApiEnabled), so a direct
dispatch on released imsg fails closed with a clear message instead of an
opaque CLI rejection. Adds a negative handleAction test.
* fix(imessage): harden native poll support
* fix(message): validate targets before channel discovery
* fix(message): validate targets before channel discovery
---------
Co-authored-by: Omar Shahine <lobster@users.noreply.github.com>
Co-authored-by: Omar Shahine <10343873+omarshahine@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* feat(openai): add GPT-5.6 series support
* docs: refresh map for GPT-5.6
* fix(openai): preserve GPT-5.6 thinking metadata
* fix(codex): sync managed app server version
* fix(codex): sync managed app server version
* fix(openai): account for GPT-5.6 cache writes
---------
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
* fix(acpx): consume acpx 0.11.1 model capability errors
* fix(acpx): refresh npm shrinkwrap for 0.11.1
* test: include workflow checks in tooling plan
Merged under maintainer approval while exact-head CI was queue-bound.
Co-authored-by: Developers Digest <124798203+developersdigest@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@users.noreply.github.com>
One-time maintainer-authorized bootstrap merge for the release-gate verifier policy. Exact hosted CI and all supporting workflow gates passed on 66133de419.