Skip to content

feat(context-engine): add sessionKey to all ContextEngine interface methods#12

Merged
davidrudduck merged 1 commit intomainfrom
feat/context-engine-session-key
Mar 24, 2026
Merged

feat(context-engine): add sessionKey to all ContextEngine interface methods#12
davidrudduck merged 1 commit intomainfrom
feat/context-engine-session-key

Conversation

@davidrudduck
Copy link
Copy Markdown
Owner

@davidrudduck davidrudduck commented Mar 13, 2026

Summary

  • Adds optional sessionKey?: string parameter to all 6 ContextEngine lifecycle methods: bootstrap, assemble, compact, ingest, ingestBatch, and afterTurn
  • sessionKey is the canonical session identity (e.g. agent:main:whatsapp:direct:user123) that persists across session resets, unlike sessionId which rotates on each reset
  • Enables context engines to maintain continuity and external state keyed by session identity

Changes

  • src/context-engine/types.ts — Add sessionKey?: string with JSDoc to all method param types
  • src/agents/pi-embedded-runner/run/attempt.ts — Thread sessionKey through bootstrap, assemble, afterTurn, ingest, and ingestBatch call sites
  • src/context-engine/context-engine.test.ts — 7 new tests validating sessionKey propagation through each method + backward compatibility

Backward compatibility

Fully backward compatible. sessionKey is optional in all method signatures. Existing ContextEngine implementations that don't expect it will continue to work unchanged (TypeScript structural typing).

Test plan

  • 7 new tests in context-engine.test.ts (bootstrap, ingest, ingestBatch, afterTurn, assemble, compact receive sessionKey + backward compat)
  • 32/32 context-engine tests passing
  • TypeScript clean (pnpm tsgo)

Summary by CodeRabbit

  • Refactor

    • Standardized session key parameter handling across context engine methods for improved consistency.
  • Tests

    • Added comprehensive session key propagation test suite to verify correct handling across engine methods.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

The changes systematize how sessionKey is threaded through context engine method calls by reordering parameter positions, updating type definitions with documentation, and expanding tests to verify sessionKey propagation across mock engine methods.

Changes

Cohort / File(s) Summary
Context Engine Type Definitions
src/context-engine/types.ts
Updated ContextEngine interface methods (bootstrap, ingest, ingestBatch, afterTurn, assemble, compact) to relocate sessionKey?: string parameter position within params objects and added JSDoc documentation describing it as a canonical session key format.
Context Engine Test Suite
src/context-engine/context-engine.test.ts
Enhanced MockContextEngine with lastSessionKeys field to capture session keys across methods; updated method signatures to accept optional sessionKey and updated parameter shapes for ingest, assemble, and compact; added new sessionKey propagation test suite asserting that methods receive and record sessionKey values correctly.
Embedded Runner Integration
src/agents/pi-embedded-runner/run/attempt.ts
Reordered arguments passed to params.contextEngine.assemble(), relocating sessionKey parameter to align with updated interface contract.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A rabbit's delight in organization!

The sessionKey hops through channels neat,
From types to tests, a coordinated beat,
Parameters aligned in proper place,
Documentation adds a JSDoc grace,
Mock engines capture keys with care,
Propagation checked everywhere!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description covers the main problem, solution, and changes, but is missing most required template sections like change type, scope, security impact, and verification details. Complete the full template by adding change type selections, scope areas, security impact assessment, and verification/testing evidence sections.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and accurately summarizes the main change: adding sessionKey parameter to all ContextEngine interface methods.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/context-engine-session-key

Comment @coderabbitai help to get the list of available commands and usage tips.

@davidrudduck davidrudduck force-pushed the main branch 6 times, most recently from 7993143 to 75da3d4 Compare March 20, 2026 07:04
@davidrudduck davidrudduck force-pushed the main branch 3 times, most recently from 6b3ede2 to e10021e Compare March 23, 2026 07:21
@davidrudduck davidrudduck force-pushed the feat/context-engine-session-key branch from a98d015 to 3b7f9e9 Compare March 24, 2026 06:41
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.agents/skills/openclaw-release-maintainer/SKILL.md (1)

14-234: ⚠️ Potential issue | 🟠 Major

PR scope appears unrelated to the stated objective.

These changes are a substantial release-runbook rewrite, but the PR objective is sessionKey plumbing for ContextEngine interfaces and tests. This looks like a scope mismatch that can make release review/rollback harder. Please split this into a separate docs/process PR (or update the PR objective/title to match what’s actually being shipped).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-release-maintainer/SKILL.md around lines 14 - 234,
Summary: The PR mixes an unrelated release-runbook rewrite (SKILL.md) with the
intended code change for sessionKey plumbing in ContextEngine interfaces/tests;
split or re-scope. Action: remove the large SKILL.md/doc changes from this
branch or move them into a separate PR so the code-focused PR only contains the
sessionKey plumbing and related tests; alternatively, update this PR's title and
description to clearly state it includes the release-runbook rewrite and ensure
review/CI expectations are adjusted. Locate edits in
.agents/skills/openclaw-release-maintainer/SKILL.md (the release runbook
content) and either revert that file from this branch or extract its changes to
a new branch/PR, then re-run CI and update reviewers accordingly. Ensure
references to "sessionKey", "ContextEngine" and related test names remain in the
original PR and that the new docs PR has its own changelog/commit message
describing the runbook rewrite.
🧹 Nitpick comments (7)
docs/channels/feishu.md (1)

667-691: Consider adding channels.feishu.accounts.<id>.name to the configuration reference table.

The examples now use name, but the reference matrix does not list it yet. Adding it will prevent reader confusion when they jump straight to the reference section.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/channels/feishu.md` around lines 667 - 691, Add a new row to the Feishu
configuration reference table documenting channels.feishu.accounts.<id>.name:
specify the setting key as `channels.feishu.accounts.<id>.name`, a short
description like "Account display name" (or "Human-readable account name"), and
indicate the default (e.g., `-` or `none`) so the table matches examples that
reference name and avoids reader confusion; update the surrounding rows if
needed to keep table alignment consistent.
.agents/skills/openclaw-parallels-smoke/SKILL.md (1)

26-26: Consider date-stamping host-specific fallback guidance.

Line 26 uses “current host”/“today”, which will go stale quickly in an operational skill doc. Prefer an explicit date marker (e.g., “as of 24 March 2026”) to reduce ambiguity during future reruns.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-parallels-smoke/SKILL.md at line 26, Update the
ambiguous "current host"/"today" phrasing in the SKILL.md sentence that
references the aggregate npm-update wrapper and the fallback from "Ubuntu
24.04.3 ARM64" to "Ubuntu 25.10" (and the related `parallels-linux-smoke.sh`
behavior) by replacing it with an explicit date stamp (e.g., "as of 24 March
2026") so the fallback guidance remains unambiguous for future runs; ensure the
new text mentions the exact date and preserves the same fallback policy and
referenced distro strings.
.github/workflows/openclaw-npm-release.yml (1)

121-129: Redundant tag validation.

This tag validation step is identical to lines 31-39 in the preflight_openclaw_npm job, and publish_openclaw_npm already depends on preflight_openclaw_npm completing successfully. The duplication is harmless (defence in depth) but adds ~1 step of execution time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/openclaw-npm-release.yml around lines 121 - 129, Remove
the redundant "Validate tag input format" step from the publish_openclaw_npm job
since the same validation already runs in the preflight_openclaw_npm job that
publish_openclaw_npm depends on; locate the step named "Validate tag input
format" (the block that sets RELEASE_TAG and runs the regex check) inside the
publish_openclaw_npm job and delete it so validation only occurs once in
preflight_openclaw_npm, ensuring the publish job still relies on the preflight
job's success.
apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift (1)

565-599: Consider extracting shared audit-append logic.

appendConfigObserveAudit (lines 565-599) is nearly identical to appendConfigWriteAudit (lines 529-563), differing only in the "event" field value. Consider extracting a shared helper to reduce duplication.

♻️ Proposed refactor
private static func appendConfigAudit(event: String, fields: [String: Any]) {
    var record: [String: Any] = [
        "ts": ISO8601DateFormatter().string(from: Date()),
        "source": "macos-openclaw-config-file",
        "event": event,
        "pid": ProcessInfo.processInfo.processIdentifier,
        "argv": Array(ProcessInfo.processInfo.arguments.prefix(8)),
    ]
    for (key, value) in fields {
        record[key] = value is NSNull ? NSNull() : value
    }
    guard JSONSerialization.isValidJSONObject(record),
          let data = try? JSONSerialization.data(withJSONObject: record)
    else {
        return
    }
    var line = Data()
    line.append(data)
    line.append(0x0A)
    let logURL = self.configAuditLogURL()
    do {
        try FileManager().createDirectory(
            at: logURL.deletingLastPathComponent(),
            withIntermediateDirectories: true)
        if !FileManager().fileExists(atPath: logURL.path) {
            FileManager().createFile(atPath: logURL.path, contents: nil)
        }
        let handle = try FileHandle(forWritingTo: logURL)
        defer { try? handle.close() }
        try handle.seekToEnd()
        try handle.write(contentsOf: line)
    } catch {
        // best-effort
    }
}

private static func appendConfigWriteAudit(_ fields: [String: Any]) {
    self.appendConfigAudit(event: "config.write", fields: fields)
}

private static func appendConfigObserveAudit(_ fields: [String: Any]) {
    self.appendConfigAudit(event: "config.observe", fields: fields)
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift` around lines 565 - 599,
appendConfigObserveAudit duplicates appendConfigWriteAudit except for the
"event" value; extract the shared logic into a new helper (e.g., private static
func appendConfigAudit(event: String, fields: [String: Any])) that builds the
record, validates/serializes JSON, appends the newline, and writes to the file
using configAuditLogURL(); then implement appendConfigWriteAudit and
appendConfigObserveAudit as thin callers that pass "config.write" and
"config.observe" respectively to appendConfigAudit to remove duplication.
.github/workflows/upstream-sync.yml (1)

58-64: Manual resolution instructions should recommend --force-with-lease.

The resolution instructions suggest git push origin main after manual rebase, but this could overwrite concurrent changes if another contributor pushed to main in the meantime. Consider updating to use --force-with-lease for safety.

📝 Proposed fix
              "**To resolve:**",
              "```bash",
              "git fetch upstream main",
              "git rebase upstream/main",
              "# resolve any conflicts, then:",
-             "git push origin main",
+             "git push origin main --force-with-lease",
              "```",
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/upstream-sync.yml around lines 58 - 64, Update the manual
resolution bash snippet so the final push uses a safer force option: locate the
quoted command string "git push origin main" in the upstream-sync workflow's
bash block and replace it with "git push origin main --force-with-lease" so
rebased branches are pushed without risking overwriting concurrent remote
changes.
.agents/skills/openclaw-release-maintainer/SKILL.md (2)

89-114: Consider reducing duplicated release rules across sections.

The same constraints (preflight pass criteria, dispatch constraints, publish ordering) are repeated in three sections. A single source checklist with cross-links would reduce drift risk and keep future edits safer.

Also applies to: 117-166, 198-234

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-release-maintainer/SKILL.md around lines 89 - 114,
The repeated release constraints under "Check all relevant release builds" and
the other duplicated blocks (referenced at ranges 117-166 and 198-234) should be
consolidated into a single canonical checklist section (e.g., "Release Preflight
Checklist") and the other sections should be replaced with short cross-links
pointing to that canonical list; update references to specific items (preflight
pass criteria, dispatch constraints, publish ordering, appcast rules) to link
into the single checklist, remove duplicate bullet lists in the other headings,
and ensure any section that relied on the original bullets references the
checklist entries by name so future edits only change one source of truth.

112-113: Add an explicit exception path for urgent stable releases.

“Confirm the latest beta already passed…” reads as a hard gate. That may block urgent stable hotfixes when no beta exists. Consider stating an explicit exception/override condition so operators have a clear path under incident pressure.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-release-maintainer/SKILL.md around lines 112 - 113,
Update the two bullets in SKILL.md that read “For stable releases, confirm the
latest beta already passed the broader release workflows before cutting stable.”
and “If any required build, packaging step, or release workflow is red, do not
say the release is ready.” to add an explicit exception clause for urgent
hotfixes: add a short conditional sentence that permits cutting a stable hotfix
when an incident override is authorized (e.g., by an on-call engineer or release
manager) and document required safeguards (manual QA checklist, rollback plan,
and communication steps) that must be completed before using the override;
ensure the wording makes this an exceptional, auditable path rather than a
routine bypass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.agents/skills/openclaw-parallels-smoke/SKILL.md:
- Line 19: The runbook mixes two orders for invoking prlctl exec which causes
operator confusion; normalize all examples to the canonical form prlctl exec
"$VM" --current-user ... by finding every occurrence of prlctl exec and changing
instances that place --current-user before the VM argument so the VM name (e.g.,
"$VM") always immediately follows prlctl exec and --current-user comes after it;
update examples, usage lines and any inline snippets to use this single
canonical ordering.
- Line 25: Edit the sentence that begins "Same-guest update verification should
set the default model explicitly to `openai/gpt-5.4` before the agent turn and
use a fresh explicit `--session-id` so old session model state does not leak
into the check." to insert a comma before "and use" so it reads "...before the
agent turn, and use a fresh explicit `--session-id`..." to improve clarity in
SKILL.md.

In @.agents/skills/openclaw-release-maintainer/SKILL.md:
- Around line 37-38: The document alternates between two correction-tag formats
(`vYYYY.M.D-N` and `YYYY.M.D-N`); pick the canonical format (include the leading
"v" if that's the chosen standard) and replace all occurrences so the file uses
a single consistent tag style (update the references in the fallback
correction-tag sentence around the block with `For fallback correction tags like
...` and the other mentions around lines referenced, e.g., the section covering
“Bump version everywhere” and the occurrences noted at 85-87) so every example,
instruction, and variable uses the same format.

In @.github/pull_request_template.md:
- Around line 38-48: Update the template sentences that start with the
conjunctive adverb "Otherwise" to include the missing comma for correct grammar;
specifically edit the lines containing "For bug fixes or regressions, explain
why this happened, not just what changed. Otherwise write `N/A`." and "For bug
fixes or regressions, name the smallest reliable test coverage that should have
caught this. Otherwise write `N/A`." so each "Otherwise" is followed by a comma.

In @.github/workflows/ci.yml:
- Around line 10-11: The workflow currently sets cancel-in-progress: true
unconditionally which will cancel in-progress push builds; change the condition
so cancel-in-progress is only true for pull_request events and false for push
events (use the same event checks around the existing group expression),
targeting the cancel-in-progress key in the CI workflow to preserve in-progress
push builds while still cancelling PR runs.

In `@apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift`:
- Around line 482-488: The code currently treats NSNull() as a present value
because entry["lastObservedSuspiciousSignature"] != nil is true for NSNull;
update the condition so the health state is only written when a real suspicious
signature exists (not nil and not NSNull). Modify the if that uses
sameFingerprint(...) and entry["lastObservedSuspiciousSignature"] to check both
nil and NSNull (e.g., require entry["lastObservedSuspiciousSignature"] != nil &&
!(entry["lastObservedSuspiciousSignature"] is NSNull)) before calling
setConfigHealthEntry and writeConfigHealthState; reference functions
sameFingerprint, setConfigHealthEntry, writeConfigHealthState and the key
entry["lastObservedSuspiciousSignature"] when making the change.

In `@apps/macos/Tests/OpenClawIPCTests/OpenClawConfigFileTests.swift`:
- Around line 197-200: The test currently expects a trailing newline when
comparing the file contents: update the assertion in the block that reads
`preserved` (the String from `clobberedPath`) so it compares `preserved` to the
`clobbered` value without appending `\n`; locate the `if let clobberedPath`
block and change the expectation from `preserved == "\(clobbered)\n"` to
`preserved == "\(clobbered)"` so the test matches the exact written string.

In `@docs/.generated/config-baseline.json`:
- Around line 31100-31109: The new config entry
channels.synology-chat.dangerouslyAllowInheritedWebhookPath is missing
user-facing metadata; add a "label" (e.g., "Allow inherited webhook path
(dangerous)") and a "help" string that clearly explains the risk and intended
use (e.g., warns about security implications, when to enable, and examples), and
include these properties in the JSON object for that key so docs and UI can
render a descriptive name and tooltip.

In `@docs/cli/index.md`:
- Around line 137-140: The Plugins command list is missing documentation entries
for the new commands; add matching "uninstall" and "update" subsections/entries
in the Plugins section to document their usage, flags, and examples so the
top-level command list (the entries "uninstall" and "update") matches the
detailed Plugin docs; update any nearby examples or the summary table that
references plugin commands to include uninstall and update as well.
- Around line 150-151: The docs mention the "broadcast" subcommand under the
message tree but the message reference list omits it; update the message
subcommand reference block to include the "broadcast" entry (label it as message
broadcast) so the commands list matches the tree and provides consistent
guidance for the message command set (look for the "message" subcommand list and
add an entry for "broadcast").

---

Outside diff comments:
In @.agents/skills/openclaw-release-maintainer/SKILL.md:
- Around line 14-234: Summary: The PR mixes an unrelated release-runbook rewrite
(SKILL.md) with the intended code change for sessionKey plumbing in
ContextEngine interfaces/tests; split or re-scope. Action: remove the large
SKILL.md/doc changes from this branch or move them into a separate PR so the
code-focused PR only contains the sessionKey plumbing and related tests;
alternatively, update this PR's title and description to clearly state it
includes the release-runbook rewrite and ensure review/CI expectations are
adjusted. Locate edits in .agents/skills/openclaw-release-maintainer/SKILL.md
(the release runbook content) and either revert that file from this branch or
extract its changes to a new branch/PR, then re-run CI and update reviewers
accordingly. Ensure references to "sessionKey", "ContextEngine" and related test
names remain in the original PR and that the new docs PR has its own
changelog/commit message describing the runbook rewrite.

---

Nitpick comments:
In @.agents/skills/openclaw-parallels-smoke/SKILL.md:
- Line 26: Update the ambiguous "current host"/"today" phrasing in the SKILL.md
sentence that references the aggregate npm-update wrapper and the fallback from
"Ubuntu 24.04.3 ARM64" to "Ubuntu 25.10" (and the related
`parallels-linux-smoke.sh` behavior) by replacing it with an explicit date stamp
(e.g., "as of 24 March 2026") so the fallback guidance remains unambiguous for
future runs; ensure the new text mentions the exact date and preserves the same
fallback policy and referenced distro strings.

In @.agents/skills/openclaw-release-maintainer/SKILL.md:
- Around line 89-114: The repeated release constraints under "Check all relevant
release builds" and the other duplicated blocks (referenced at ranges 117-166
and 198-234) should be consolidated into a single canonical checklist section
(e.g., "Release Preflight Checklist") and the other sections should be replaced
with short cross-links pointing to that canonical list; update references to
specific items (preflight pass criteria, dispatch constraints, publish ordering,
appcast rules) to link into the single checklist, remove duplicate bullet lists
in the other headings, and ensure any section that relied on the original
bullets references the checklist entries by name so future edits only change one
source of truth.
- Around line 112-113: Update the two bullets in SKILL.md that read “For stable
releases, confirm the latest beta already passed the broader release workflows
before cutting stable.” and “If any required build, packaging step, or release
workflow is red, do not say the release is ready.” to add an explicit exception
clause for urgent hotfixes: add a short conditional sentence that permits
cutting a stable hotfix when an incident override is authorized (e.g., by an
on-call engineer or release manager) and document required safeguards (manual QA
checklist, rollback plan, and communication steps) that must be completed before
using the override; ensure the wording makes this an exceptional, auditable path
rather than a routine bypass.

In @.github/workflows/openclaw-npm-release.yml:
- Around line 121-129: Remove the redundant "Validate tag input format" step
from the publish_openclaw_npm job since the same validation already runs in the
preflight_openclaw_npm job that publish_openclaw_npm depends on; locate the step
named "Validate tag input format" (the block that sets RELEASE_TAG and runs the
regex check) inside the publish_openclaw_npm job and delete it so validation
only occurs once in preflight_openclaw_npm, ensuring the publish job still
relies on the preflight job's success.

In @.github/workflows/upstream-sync.yml:
- Around line 58-64: Update the manual resolution bash snippet so the final push
uses a safer force option: locate the quoted command string "git push origin
main" in the upstream-sync workflow's bash block and replace it with "git push
origin main --force-with-lease" so rebased branches are pushed without risking
overwriting concurrent remote changes.

In `@apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift`:
- Around line 565-599: appendConfigObserveAudit duplicates
appendConfigWriteAudit except for the "event" value; extract the shared logic
into a new helper (e.g., private static func appendConfigAudit(event: String,
fields: [String: Any])) that builds the record, validates/serializes JSON,
appends the newline, and writes to the file using configAuditLogURL(); then
implement appendConfigWriteAudit and appendConfigObserveAudit as thin callers
that pass "config.write" and "config.observe" respectively to appendConfigAudit
to remove duplication.

In `@docs/channels/feishu.md`:
- Around line 667-691: Add a new row to the Feishu configuration reference table
documenting channels.feishu.accounts.<id>.name: specify the setting key as
`channels.feishu.accounts.<id>.name`, a short description like "Account display
name" (or "Human-readable account name"), and indicate the default (e.g., `-` or
`none`) so the table matches examples that reference name and avoids reader
confusion; update the surrounding rows if needed to keep table alignment
consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3aa309e9-f354-452e-9b88-7329fb071754

📥 Commits

Reviewing files that changed from the base of the PR and between a98d015 and 3b7f9e9.

📒 Files selected for processing (36)
  • .agents/skills/openclaw-parallels-smoke/SKILL.md
  • .agents/skills/openclaw-release-maintainer/SKILL.md
  • .github/labeler.yml
  • .github/pull_request_template.md
  • .github/workflows/ci-bun.yml
  • .github/workflows/ci.yml
  • .github/workflows/macos-release.yml
  • .github/workflows/openclaw-npm-release.yml
  • .github/workflows/upstream-sync.yml
  • CHANGELOG.md
  • appcast.xml
  • apps/android/app/build.gradle.kts
  • apps/android/app/src/main/java/ai/openclaw/app/MainViewModel.kt
  • apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt
  • apps/android/app/src/main/java/ai/openclaw/app/node/A2UIHandler.kt
  • apps/android/app/src/main/java/ai/openclaw/app/node/CanvasActionTrust.kt
  • apps/android/app/src/main/java/ai/openclaw/app/ui/CanvasScreen.kt
  • apps/android/app/src/test/java/ai/openclaw/app/node/CanvasActionTrustTest.kt
  • apps/ios/Config/Version.xcconfig
  • apps/ios/fastlane/Fastfile
  • apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift
  • apps/macos/Sources/OpenClaw/Resources/Info.plist
  • apps/macos/Tests/OpenClawIPCTests/OpenClawConfigFileTests.swift
  • docs/.generated/config-baseline.json
  • docs/.generated/config-baseline.jsonl
  • docs/.generated/plugin-sdk-api-baseline.json
  • docs/.generated/plugin-sdk-api-baseline.jsonl
  • docs/automation/cron-jobs.md
  • docs/automation/troubleshooting.md
  • docs/automation/webhook.md
  • docs/channels/feishu.md
  • docs/channels/index.md
  • docs/cli/cron.md
  • docs/cli/index.md
  • docs/cli/node.md
  • docs/docs.json
✅ Files skipped from review due to trivial changes (12)
  • .github/labeler.yml
  • docs/automation/webhook.md
  • docs/cli/node.md
  • docs/cli/cron.md
  • apps/ios/fastlane/Fastfile
  • apps/android/app/build.gradle.kts
  • apps/macos/Sources/OpenClaw/Resources/Info.plist
  • apps/ios/Config/Version.xcconfig
  • .github/workflows/ci-bun.yml
  • CHANGELOG.md
  • docs/.generated/plugin-sdk-api-baseline.json
  • docs/.generated/plugin-sdk-api-baseline.jsonl


- Preferred entrypoint: `pnpm test:parallels:npm-update`
- Flow: fresh snapshot -> install npm package baseline -> smoke -> install current main tgz on the same guest -> smoke again.
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add a comma for clarity in the update verification sentence.

Line 25 reads more clearly with a comma before “and use a fresh explicit --session-id”.

Proposed doc tweak
-- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
+- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn, and use a fresh explicit `--session-id` so old session model state does not leak into the check.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh explicit `--session-id` so old session model state does not leak into the check.
- Same-guest update verification should set the default model explicitly to `openai/gpt-5.4` before the agent turn, and use a fresh explicit `--session-id` so old session model state does not leak into the check.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~25-~25: Possible missing comma found.
Context: ...ly to openai/gpt-5.4 before the agent turn and use a fresh explicit --session-id...

(AI_HYDRA_LEO_MISSING_COMMA)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-parallels-smoke/SKILL.md at line 25, Edit the
sentence that begins "Same-guest update verification should set the default
model explicitly to `openai/gpt-5.4` before the agent turn and use a fresh
explicit `--session-id` so old session model state does not leak into the
check." to insert a comma before "and use" so it reads "...before the agent
turn, and use a fresh explicit `--session-id`..." to improve clarity in
SKILL.md.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — apologies for the noise. The comment was raised against diff content from the bad rebase and doesn't apply to the current clean commit. I'll disregard it.

Comment on lines +37 to 38
- For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`.
- “Bump version everywhere” means all version locations above except `appcast.xml`.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use one canonical correction-tag format throughout.

The doc alternates between vYYYY.M.D-N and YYYY.M.D-N. That inconsistency can cause wrong tag/release inputs during dispatch. Please standardise on one format (including v prefix if that’s canonical) in all sections.

Proposed doc fix
-- For stable correction releases like `YYYY.M.D-N`, it also verifies the
-  upgrade path from `YYYY.M.D` to `YYYY.M.D-N` so a correction publish cannot
+- For stable correction releases like `vYYYY.M.D-N`, it also verifies the
+  upgrade path from `YYYY.M.D` to `vYYYY.M.D-N` so a correction publish cannot

Also applies to: 85-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.agents/skills/openclaw-release-maintainer/SKILL.md around lines 37 - 38,
The document alternates between two correction-tag formats (`vYYYY.M.D-N` and
`YYYY.M.D-N`); pick the canonical format (include the leading "v" if that's the
chosen standard) and replace all occurrences so the file uses a single
consistent tag style (update the references in the fallback correction-tag
sentence around the block with `For fallback correction tags like ...` and the
other mentions around lines referenced, e.g., the section covering “Bump version
everywhere” and the occurrences noted at 85-87) so every example, instruction,
and variable uses the same format.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — apologies for the noise. The review was generated against the inflated diff from the bad rebase, so comments on files like this one (outside the intended sessionKey changes) should be disregarded entirely. Thanks for the clarification and for cleaning up the branch.

ʕ •ᴥ•ʔ

Comment on lines +38 to +48
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.

- Root cause:
- Missing detection / guardrail:
- Prior context (`git blame`, prior PR, issue, or refactor if known):
- Why this regressed now:
- If unknown, what was ruled out:

## Regression Test Plan (if applicable)

For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write `N/A`.
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Minor grammatical corrections needed.

The static analysis correctly identifies missing commas after "Otherwise" when used as a conjunctive adverb at the start of a sentence.

📝 Proposed fix
-For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.
+For bug fixes or regressions, explain why this happened, not just what changed. Otherwise, write `N/A`. If the cause is unclear, write `Unknown`.
-For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write `N/A`.
+For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise, write `N/A`.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise write `N/A`. If the cause is unclear, write `Unknown`.
- Root cause:
- Missing detection / guardrail:
- Prior context (`git blame`, prior PR, issue, or refactor if known):
- Why this regressed now:
- If unknown, what was ruled out:
## Regression Test Plan (if applicable)
For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise write `N/A`.
For bug fixes or regressions, explain why this happened, not just what changed. Otherwise, write `N/A`. If the cause is unclear, write `Unknown`.
- Root cause:
- Missing detection / guardrail:
- Prior context (`git blame`, prior PR, issue, or refactor if known):
- Why this regressed now:
- If unknown, what was ruled out:
## Regression Test Plan (if applicable)
For bug fixes or regressions, name the smallest reliable test coverage that should have caught this. Otherwise, write `N/A`.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~38-~38: A comma may be missing after the conjunctive/linking adverb ‘Otherwise’.
Context: ...y this happened, not just what changed. Otherwise write N/A. If the cause is unclear, w...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[uncategorized] ~48-~48: A comma may be missing after the conjunctive/linking adverb ‘Otherwise’.
Context: ... coverage that should have caught this. Otherwise write N/A. - Coverage level that sho...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/pull_request_template.md around lines 38 - 48, Update the template
sentences that start with the conjunctive adverb "Otherwise" to include the
missing comma for correct grammar; specifically edit the lines containing "For
bug fixes or regressions, explain why this happened, not just what changed.
Otherwise write `N/A`." and "For bug fixes or regressions, name the smallest
reliable test coverage that should have caught this. Otherwise write `N/A`." so
each "Otherwise" is followed by a comma.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — thanks for the context. The review was generated against the pre-force-push state that included the unintended upstream commits. Since .github/pull_request_template.md is not part of the actual sessionKey changes, this comment can be disregarded. Apologies for the noise.

Comment on lines +10 to +11
group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-push-{0}', github.ref_name) }}
cancel-in-progress: true
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Unconditional cancel-in-progress: true may interrupt main builds.

Previously, cancel-in-progress was likely conditional to avoid cancelling in-progress push builds on main. With this change, if two commits are pushed to main in quick succession (e.g., merge queue), the first build will be cancelled. This could leave the main branch in a state where no completed CI run exists for a given commit.

Consider whether this is intentional or if push events should preserve the previous in-progress build.

🛡️ Potential fix to preserve push builds
 concurrency:
   group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-push-{0}', github.ref_name) }}
-  cancel-in-progress: true
+  cancel-in-progress: ${{ github.event_name == 'pull_request' }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-push-{0}', github.ref_name) }}
cancel-in-progress: true
group: ${{ github.event_name == 'pull_request' && format('ci-pr-{0}', github.event.pull_request.number) || format('ci-push-{0}', github.ref_name) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/ci.yml around lines 10 - 11, The workflow currently sets
cancel-in-progress: true unconditionally which will cancel in-progress push
builds; change the condition so cancel-in-progress is only true for pull_request
events and false for push events (use the same event checks around the existing
group expression), targeting the cancel-in-progress key in the CI workflow to
preserve in-progress push builds while still cancelling PR runs.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — thanks for the context. My review comment was made against the stale diff from the bad rebase and is not relevant to this PR. Apologies for the noise.

Comment on lines +482 to +488
"lastObservedSuspiciousSignature": NSNull(),
]
if !self.sameFingerprint(lastKnownGood, current) || entry["lastObservedSuspiciousSignature"] != nil {
state = self.setConfigHealthEntry(state: state, configPath: configURL.path, entry: nextEntry)
self.writeConfigHealthState(state)
}
return
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential issue with NSNull comparison.

At line 484, the condition entry["lastObservedSuspiciousSignature"] != nil will be true even when the value is NSNull() (set at line 482), since NSNull is not nil. This means the health state will always be written on valid reads, even when no suspicious signature was previously recorded.

Consider checking for both nil and NSNull:

🐛 Proposed fix
-            if !self.sameFingerprint(lastKnownGood, current) || entry["lastObservedSuspiciousSignature"] != nil {
+            let hadSuspiciousSignature = entry["lastObservedSuspiciousSignature"] != nil &&
+                !(entry["lastObservedSuspiciousSignature"] is NSNull)
+            if !self.sameFingerprint(lastKnownGood, current) || hadSuspiciousSignature {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/macos/Sources/OpenClaw/OpenClawConfigFile.swift` around lines 482 - 488,
The code currently treats NSNull() as a present value because
entry["lastObservedSuspiciousSignature"] != nil is true for NSNull; update the
condition so the health state is only written when a real suspicious signature
exists (not nil and not NSNull). Modify the if that uses sameFingerprint(...)
and entry["lastObservedSuspiciousSignature"] to check both nil and NSNull (e.g.,
require entry["lastObservedSuspiciousSignature"] != nil &&
!(entry["lastObservedSuspiciousSignature"] is NSNull)) before calling
setConfigHealthEntry and writeConfigHealthState; reference functions
sameFingerprint, setConfigHealthEntry, writeConfigHealthState and the key
entry["lastObservedSuspiciousSignature"] when making the change.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — apologies for the noise. The comment was raised against OpenClawConfigFile.swift which was pulled in by the bad rebase and is not part of this PR's changes. Thanks for the clarification after force-pushing to the clean commit.

Comment on lines +31100 to +31109
{
"path": "channels.synology-chat.dangerouslyAllowInheritedWebhookPath",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add label and help for the new dangerous Synology Chat toggle.

Lines 31100-31109 introduce a high-risk config key but omit user-facing metadata. That makes docs/UI rendering less clear for operators.

Suggested patch
     {
       "path": "channels.synology-chat.dangerouslyAllowInheritedWebhookPath",
       "kind": "channel",
       "type": "boolean",
       "required": false,
       "deprecated": false,
       "sensitive": false,
       "tags": [],
+      "label": "Allow Inherited Webhook Path (Dangerous)",
+      "help": "Allows inherited webhook path behaviour for compatibility. Keep disabled unless explicitly required.",
       "hasChildren": false
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"path": "channels.synology-chat.dangerouslyAllowInheritedWebhookPath",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"hasChildren": false
},
{
"path": "channels.synology-chat.dangerouslyAllowInheritedWebhookPath",
"kind": "channel",
"type": "boolean",
"required": false,
"deprecated": false,
"sensitive": false,
"tags": [],
"label": "Allow Inherited Webhook Path (Dangerous)",
"help": "Allows inherited webhook path behaviour for compatibility. Keep disabled unless explicitly required.",
"hasChildren": false
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/.generated/config-baseline.json` around lines 31100 - 31109, The new
config entry channels.synology-chat.dangerouslyAllowInheritedWebhookPath is
missing user-facing metadata; add a "label" (e.g., "Allow inherited webhook path
(dangerous)") and a "help" string that clearly explains the risk and intended
use (e.g., warns about security implications, when to enable, and examples), and
include these properties in the JSON object for that key so docs and UI can
render a descriptive name and tooltip.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, thanks for the clarification. You're right — the comment was mistakenly raised against upstream changes pulled in during the bad rebase, not against anything introduced by this PR. I'll disregard it. The force-push to the clean single commit resolves the confusion.

Comment on lines +137 to +140
inspect
install
uninstall
update
Copy link
Copy Markdown

@coderabbitai coderabbitai bot Mar 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Plugin tree now outpaces the detailed plugin docs.

Line 139 and Line 140 add uninstall/update, but the Plugins section below does not document those commands yet. Please add matching entries so the reference stays internally consistent.

Suggested doc patch
## Plugins

- `openclaw plugins list` — discover plugins (use `--json` for machine output).
- `openclaw plugins inspect <id>` — show details for a plugin (`info` is an alias).
- `openclaw plugins install <path|.tgz|npm-spec|plugin@marketplace>` — install a plugin (or add a plugin path to `plugins.load.paths`).
+- `openclaw plugins uninstall <id>` — remove an installed plugin.
+- `openclaw plugins update <id|--all>` — update one plugin or all managed plugins.
- `openclaw plugins marketplace list <marketplace>` — list marketplace entries before install.
- `openclaw plugins enable <id>` / `disable <id>` — toggle `plugins.entries.<id>.enabled`.
- `openclaw plugins doctor` — report plugin load errors.

Also applies to: 144-144

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/cli/index.md` around lines 137 - 140, The Plugins command list is
missing documentation entries for the new commands; add matching "uninstall" and
"update" subsections/entries in the Plugins section to document their usage,
flags, and examples so the top-level command list (the entries "uninstall" and
"update") matches the detailed Plugin docs; update any nearby examples or the
summary table that references plugin commands to include uninstall and update as
well.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not applicable — this file is not part of this PR. The previous review was against a bad rebase that included ~290 upstream commits. The branch has been force-pushed to a single clean commit with only the sessionKey changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@davidrudduck, understood — apologies for the noise. The review was incorrectly triggered by the upstream commits pulled in during the bad rebase, not by anything in this PR. I'll disregard that comment entirely.

( ´•ω•`) 🐇

@davidrudduck davidrudduck force-pushed the feat/context-engine-session-key branch from 3b7f9e9 to fea89b2 Compare March 24, 2026 07:04
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/context-engine/context-engine.test.ts (1)

1029-1039: Consider expanding backward-compatibility coverage.

This test validates that sessionKey is optional for ingest, assemble, and compact. For completeness, consider also asserting the same for bootstrap, ingestBatch, and afterTurn — especially since afterTurn has a different parameter shape with runtimeContext.

That said, the current coverage is a reasonable representative sample and adequately demonstrates backward compatibility.

💡 Optional: expand optional-sessionKey test
 it("sessionKey is optional — methods work without it", async () => {
   const engine = new MockContextEngine();
+
+  await engine.bootstrap({ sessionId: "s1", sessionFile: "/tmp/s.jsonl" });
+  expect(engine.lastSessionKeys.bootstrap).toBeUndefined();
+
   await engine.ingest({ sessionId: "s1", message: makeMockMessage() });
   expect(engine.lastSessionKeys.ingest).toBeUndefined();

+  await engine.ingestBatch({ sessionId: "s1", messages: [] });
+  expect(engine.lastSessionKeys.ingestBatch).toBeUndefined();
+
+  await engine.afterTurn({
+    sessionId: "s1",
+    sessionFile: "/tmp/s.jsonl",
+    messages: [],
+    prePromptMessageCount: 0,
+  });
+  expect(engine.lastSessionKeys.afterTurn).toBeUndefined();
+
   await engine.assemble({ sessionId: "s1", messages: [] });
   expect(engine.lastSessionKeys.assemble).toBeUndefined();

   await engine.compact({ sessionId: "s1", sessionFile: "/tmp/s.jsonl" });
   expect(engine.lastSessionKeys.compact).toBeUndefined();
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/context-engine/context-engine.test.ts` around lines 1029 - 1039, Add
additional assertions to the "sessionKey is optional" test to cover bootstrap,
ingestBatch, and afterTurn on MockContextEngine: call engine.bootstrap(...) with
only sessionId, engine.ingestBatch(...) with only sessionId and messages, and
engine.afterTurn(...) with the runtimeContext shape but without sessionKey, then
assert engine.lastSessionKeys.bootstrap, .ingestBatch, and .afterTurn are
undefined; follow the same pattern used for ingest/assemble/compact so the test
verifies backward compatibility for those methods as well.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/context-engine/context-engine.test.ts`:
- Around line 1029-1039: Add additional assertions to the "sessionKey is
optional" test to cover bootstrap, ingestBatch, and afterTurn on
MockContextEngine: call engine.bootstrap(...) with only sessionId,
engine.ingestBatch(...) with only sessionId and messages, and
engine.afterTurn(...) with the runtimeContext shape but without sessionKey, then
assert engine.lastSessionKeys.bootstrap, .ingestBatch, and .afterTurn are
undefined; follow the same pattern used for ingest/assemble/compact so the test
verifies backward compatibility for those methods as well.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: c0e26b2d-c0f1-48e1-9a75-78e3a9de117f

📥 Commits

Reviewing files that changed from the base of the PR and between 3b7f9e9 and fea89b2.

📒 Files selected for processing (3)
  • src/agents/pi-embedded-runner/run/attempt.ts
  • src/context-engine/context-engine.test.ts
  • src/context-engine/types.ts
✅ Files skipped from review due to trivial changes (2)
  • src/agents/pi-embedded-runner/run/attempt.ts
  • src/context-engine/types.ts

@davidrudduck
Copy link
Copy Markdown
Owner Author

The first review (36 files) was against a bad rebase that accidentally included ~290 upstream commits. The branch has since been force-pushed to a single clean commit (fea89b28b5) containing only the sessionKey changes across 3 files:

  • src/context-engine/types.ts
  • src/context-engine/context-engine.test.ts
  • src/agents/pi-embedded-runner/run/attempt.ts

All feedback on files outside these three (.agents/skills/, .github/, apps/, docs/, etc.) is not applicable to this PR.

…ethods

Adds optional sessionKey param to bootstrap, assemble, compact, ingest,
ingestBatch, and afterTurn so context engines can identify the canonical
session identity across resets. All call sites in attempt.ts, run.ts, and
compact.ts now thread params.sessionKey through. Backward compatible —
sessionKey is optional in all method signatures.

Includes 7 new tests validating sessionKey propagation through each method.
@davidrudduck davidrudduck force-pushed the feat/context-engine-session-key branch from fea89b2 to 06d9404 Compare March 24, 2026 08:28
@davidrudduck
Copy link
Copy Markdown
Owner Author

Re: CodeRabbit's second review nitpick on expanding sessionKey optional test coverage to also cover bootstrap, ingestBatch, and afterTurn:

Acknowledged — the current test covers the representative sample (ingest, assemble, compact). The suggestion to add bootstrap, ingestBatch, and afterTurn is reasonable for completeness. Will consider expanding in a follow-up if this PR moves to upstream.

@davidrudduck davidrudduck merged commit 04c2d69 into main Mar 24, 2026
3 of 9 checks passed
@davidrudduck davidrudduck deleted the feat/context-engine-session-key branch March 24, 2026 08:30
davidrudduck pushed a commit that referenced this pull request Mar 31, 2026
* feat: add QQ Bot channel extension

* fix(qqbot): add setupWizard to runtime plugin for onboard re-entry

* fix: fix review

* fix: fix review

* chore: sync lockfile and config-docs baseline for qqbot extension

* refactor: 移除图床服务器相关代码

* fix

* docs: 新增 QQ Bot 插件文档并修正链接路径

* refactor: remove credential backup functionality and update setup logic

- Deleted the credential backup module to streamline the codebase.
- Updated the setup surface to handle client secrets more robustly, allowing for configured secret inputs.
- Simplified slash commands by removing unused hot upgrade compatibility checks and related functions.
- Adjusted types to use SecretInput for client secrets in QQBot configuration.
- Modified bundled plugin metadata to allow additional properties in the config schema.

* feat: 添加本地媒体路径解析功能,修正 QQBot 媒体路径处理

* feat: 添加本地媒体路径解析功能,修正 QQBot 媒体路径处理

* feat: remove qqbot-media and qqbot-remind skills, add tests for config and setup

- Deleted the qqbot-media and qqbot-remind skills documentation files.
- Added unit tests for qqbot configuration and setup processes, ensuring proper handling of SecretRef-backed credentials and account configurations.
- Implemented tests for local media path remapping, verifying correct resolution of media file paths.
- Removed obsolete channel and remind tools, streamlining the codebase.

* feat: 更新 QQBot 配置模式,添加音频格式和账户定义

* feat: 添加 QQBot 频道管理和定时提醒技能,更新媒体路径解析功能

* fix

* feat: 添加 /bot-upgrade 指令以查看 QQBot 插件升级指引

* feat: update reminder and qq channel skills

* feat: 更新remind工具投递目标地址格式

* feat: Refactor QQBot payload handling and improve code documentation

- Simplified and clarified the structure of payload interfaces for Cron reminders and media messages.
- Enhanced the parsing function to provide clearer error messages and improved validation.
- Updated platform utility functions for better cross-platform compatibility and clearer documentation.
- Improved text parsing utilities for better readability and consistency in emoji representation.
- Optimized upload cache management with clearer comments and reduced redundancy.
- Integrated QQBot plugin into the bundled channel plugins and updated metadata for installation.

* OK apps/macos/Sources/OpenClaw/HostEnvSecurityPolicy.generated.swift

> [email protected] check:bundled-channel-config-metadata /Users/yuehuali/code/PR/openclaw
> node --import tsx scripts/generate-bundled-channel-config-metadata.ts --check

[bundled-channel-config-metadata] stale generated output at src/config/bundled-channel-config-metadata.generated.ts
 ELIFECYCLE  Command failed with exit code 1.
 ELIFECYCLE  Command failed with exit code 1.

* feat: 添加 QQBot 渠道配置及相关账户设置

* fix(qqbot): resolve 14 high-priority bugs from PR openclaw#52986 review

DM routing (7 fixes):
- #1: DM slash-command replies use sendDmMessage(guildId) instead of sendC2CMessage(senderId)
- #2: DM qualifiedTarget uses qqbot:dm:${guildId} instead of qqbot:c2c:${senderId}
- #3: sendTextChunks adds DM branch
- #4: sendMarkdownReply adds DM branch for text and Base64 images
- #5: parseAndSendMediaTags maps DM to targetType:dm + guildId
- #6: sendTextToTarget DM branch uses sendDmMessage; MessageTarget adds guildId field
- #7: handleImage/Audio/Video/FilePayload add DM branches

Other high-priority fixes:
- #8: Fix sendC2CVoiceMessage/sendGroupVoiceMessage parameter misalignment
- #9: broadcastMessage uses groupOpenid instead of member_openid for group users
- #10: Unify KnownUser storage - proactive.ts delegates to known-users.ts
- #11: Remove invalid recordKnownUser calls for guild/DM users
- #12: sendGroupMessage uses sendAndNotify to trigger onMessageSent hook
- #13: sendPhoto channel unsupported returns error field
- #14: sendTextAfterMedia adds channel and dm branches

Type fixes:
- DeliverEventContext adds guildId field
- MediaTargetContext.targetType adds dm variant
- sendPlainTextReply imgMediaTarget adds DM branch

* fix(qqbot): resolve 2 blockers + 7 medium-priority bugs from PR openclaw#52986 review

Blocker-1: Remove unused dmPolicy config knob
- dmPolicy was declared in schema/types/plugin.json but never consumed at runtime
- Removed from config-schema.ts, types.ts, and openclaw.plugin.json
- allowFrom remains active (already wired into framework command-auth)

Blocker-2: Gate sensitive slash commands with allowFrom authorization
- SlashCommand interface adds requireAuth?: boolean
- SlashCommandContext adds commandAuthorized: boolean
- /bot-logs set to requireAuth: true (reads local log files)
- matchSlashCommand rejects unauthorized senders for requireAuth commands
- trySlashCommandOrEnqueue computes commandAuthorized from allowFrom config

Medium-priority fixes:
- #15: Strip non-HTTP/non-local markdown image tags to prevent path leakage
- openclaw#16: applyQQBotAccountConfig clears clientSecret when setting clientSecretFile and vice versa
- openclaw#17: getAdminMarkerFile sanitizes accountId to prevent path traversal
- openclaw#18: URGENT_COMMANDS uses exact match instead of startsWith prefix match
- openclaw#19: isCronExpression validates each token starts with a cron-valid character
- openclaw#20: --token format validation rejects malformed input without colon separator
- openclaw#21: resolveDefaultQQBotAccountId checks QQBOT_APP_ID environment variable

* test(qqbot): add focused tests for slash command authorization path

- Unauthorized sender rejected for /bot-logs (requireAuth: true)
- Authorized sender allowed for /bot-logs
- Non-requireAuth commands (/bot-ping, /bot-help, /bot-version) work for all senders
- Unknown slash commands return null (passthrough)
- Non-slash messages return null
- Usage query (/bot-logs ?) also gated by auth check

* fix(qqbot): align global TTS fallback with framework config resolution

- Extract isGlobalTTSAvailable to utils/audio-convert.ts, mirroring core
  resolveTtsConfig logic: check auto !== 'off', fall back to legacy
  enabled boolean, default to off when neither is set.
- Add pre-check in reply-dispatcher before calling globalTextToSpeech to
  avoid unnecessary TTS calls and noisy error logs when TTS is not
  configured.
- Remove inline as any casts; use OpenClawConfig type throughout.
- Refactor handleAudioPayload into flat early-return structure with
  unified send path (plugin TTS → global fallback → send).

* fix(qqbot): break ESM circular dependency causing multi-account startup crash

The bundled gateway chunk had a circular static import on the channel
chunk (gateway -> outbound-deliver -> channel, while channel dynamically
imports gateway). When two accounts start concurrently via Promise.all,
the first dynamic import triggers module graph evaluation; the circular
reference causes api exports (including runDiagnostics) to resolve as
undefined before the module finishes evaluating.

Fix: extract chunkText and TEXT_CHUNK_LIMIT from channel.ts into a new
text-utils.ts leaf module. outbound-deliver.ts now imports from
text-utils.ts, breaking the cycle. channel.ts re-exports for backward
compatibility.

* fix(qqbot): serialize gateway module import to prevent multi-account startup race

When multiple accounts start concurrently via Promise.all, each calls
await import('./gateway.js') independently. Due to ESM circular
dependencies in the bundled output, the first import can resolve
transitive exports as undefined before module evaluation completes.

Fix: cache the dynamic import promise in a module-level variable so all
concurrent startAccount calls share the same import, ensuring the
gateway module is fully evaluated before any account uses it.

* refactor(qqbot): remove startup greeting logic

Remove getStartupGreetingPlan and related startup greeting delivery:
- Delete startup-greeting.ts (greeting plan, marker persistence)
- Delete admin-resolver.ts (admin resolution, greeting dispatch)
- Remove startup greeting calls from gateway READY/RESUMED handlers
- Remove isFirstReadyGlobal flag and adminCtx

* fix(qqbot): skip octal escape decoding for Windows local paths

Windows paths like C:\Users\1\file.txt contain backslash-digit sequences
that were incorrectly matched as octal escape sequences and decoded,
corrupting the file path. Detect Windows local paths (drive letter or UNC
prefix) and skip the octal decoding step for them.

* fix bot issue

* feat: 支持 TTS 自动开关并清理配置中的 clientSecretFile

* docs: 添加 QQBot 配置和消息处理的设计说明

* rebase

* fix(qqbot): align slash-command auth with shared command-auth model

Route requireAuth:true slash commands (e.g. /bot-logs) through the
framework's api.registerCommand() so resolveCommandAuthorization()
applies commands.allowFrom.qqbot precedence and qqbot: prefix
normalization before any handler runs.

- slash-commands.ts: registerCommand() now auto-routes by requireAuth
  into two maps (commands / frameworkCommands); getFrameworkCommands()
  exports the auth-required set for framework registration; bot-help
  lists both maps
- index.ts: registerFull() iterates getFrameworkCommands() and calls
  api.registerCommand() for each; handler derives msgType from ctx.from,
  sends file attachments via sendDocument, supports multi-account via
  ctx.accountId
- gateway.ts (inbound): replace raw allowFrom string comparison with
  qqbotPlugin.config.formatAllowFrom() to strip qqbot: prefix and
  uppercase before matching event.senderId
- gateway.ts (pre-dispatch): remove stale auth computation; commandAuthorized
  is true (requireAuth:true commands never reach matchSlashCommand)
- command-auth.test.ts: add regression tests for qqbot: prefix
  normalization in the inbound commandAuthorized computation
- slash-commands.test.ts: update /bot-logs tests to expect null
  (command routed to framework, not in local registry)

* rebase and solve conflict

* fix(qqbot): preserve mixed env setup credentials

---------

Co-authored-by: yuehuali <[email protected]>
Co-authored-by: walli <[email protected]>
Co-authored-by: WideLee <[email protected]>
Co-authored-by: Frank Yang <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant