Skip to content

feat: add nested subagent session lineage for OpenClaw#210

Open
mnajafian-nv wants to merge 3 commits into
NVIDIA:mainfrom
mnajafian-nv:feat/openclaw-subagent-lineage-recovered
Open

feat: add nested subagent session lineage for OpenClaw#210
mnajafian-nv wants to merge 3 commits into
NVIDIA:mainfrom
mnajafian-nv:feat/openclaw-subagent-lineage-recovered

Conversation

@mnajafian-nv
Copy link
Copy Markdown
Contributor

@mnajafian-nv mnajafian-nv commented Jun 4, 2026

Overview

This PR adds nested subagent session lineage for OpenClaw so child subagent activity can nest under the requester session when stable lineage is available, while keeping replay state and emitted metadata honest when hooks arrive out of order.

  • I confirm this contribution is my own work, or I have the right to submit it under this project's license.
  • I searched existing issues and open pull requests, and this does not duplicate existing work.

Details

  • Adds OpenClaw subagent lineage tracking in the hook replay backend so child sessions can open as nested subagent scopes under the requester session when subagent_spawned provides stable parent/child lineage.
  • Introduces stable internal session ownership for replay state and alias resolution, so deferred child sessions no longer depend on mutable exported sessionId values.
  • Preserves original timestamps and queued lifecycle emissions when child session activity arrives before lineage can be resolved.
  • Materializes child-only subagent_ended paths under the child session scope instead of dropping or misparenting the terminal mark.
  • Keeps the change scoped to the OpenClaw adapter layer without changing ATOF/ATIF exporter behavior.
  • Adds focused coverage for the main subagent ordering cases, including spawn-first, child-first, child-only terminal paths, and deferred model timing followed by explicit child session_start.
  • Updates the OpenClaw integration docs to describe nested child-session behavior when stable lineage is available.

Where should the reviewer start?

Start in integrations/openclaw/src/hooks-backend.ts, especially the subagent lineage tracking and deferred child-root materialization flow. Then look at integrations/openclaw/src/hook-replay/session.ts for the stable internal session ownership model and integrations/openclaw/test/hooks-backend.test.ts for the contract-level subagent ordering cases covered here.

Related Issues: (use one of the action keywords Closes / Fixes / Resolves / Relates to)

  • Relates to OpenClaw observability consistency work.

Summary by CodeRabbit

  • Documentation

    • Updated OpenClaw plugin guide to document subagent lifecycle marks, nesting behavior, and runtime mapping updates.
  • New Features

    • Session correlation anchored to a stable owner key for more reliable replay pairing.
    • Lifecycle events and tool/LLM timings include higher-precision timestamps and deferred session-root handling to preserve lineage and ordering.
  • Tests

    • Added deterministic tests for child-session lifecycle ordering, deferred lineage materialization, and timing/correlation behavior.

Signed-off-by: mnajafian-nv <mnajafian@nvidia.com>
@mnajafian-nv mnajafian-nv added this to the 0.4 milestone Jun 4, 2026
@mnajafian-nv mnajafian-nv self-assigned this Jun 4, 2026
@mnajafian-nv mnajafian-nv added the Feature a new feature label Jun 4, 2026
@mnajafian-nv mnajafian-nv requested a review from a team as a code owner June 4, 2026 02:31
@github-actions github-actions Bot added size:L PR is large lang:js PR changes/introduces Javascript/Typescript code labels Jun 4, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Review Change Stack

Walkthrough

Refactors hook-replay to canonicalize sessions by internal ownerKey, defer session-root opening until parent lineage is available, thread microsecond timestamps through lifecycle emits and replay, add subagent lineage/promote logic, and update tests and docs to the new model.

Changes

Session identity re-anchoring and lifecycle materialization

Layer / File(s) Summary
Session identity types and resolver API
integrations/openclaw/src/hook-replay/session.ts
EnsureSessionInput and SessionState extended with ownerKey, deferred root fields (parentHandle, scopeRole, deferRootOpen, pendingRoot*, pendingCapturedEmits), and optional resolveSessionRootContext hook on SessionManager. Correlation record types (LlmInputRecord, AssistantMessageRecord, ModelCallRecord) updated to use sessionOwnerKey instead of sessionKey/sessionId. New resolveSessionOwnerKey function introduced.
Session creation and deferred root materialization
integrations/openclaw/src/hook-replay/session.ts
ensureSession refactored to resolve and initialize sessions by ownerKey, defaulting sessionId from it, with conditional deferred root opening that queues emissions instead of immediately materializing scope. New exported materializeSessionRoot function handles deferred root opening using pending timestamp/runId, proper thread scope stacking, openclaw.session_start emission, and captured emit flushing. Alias mapping updated to track ownerKey.
Session correlation eviction refactoring
integrations/openclaw/src/hook-replay/session.ts
Eviction across all correlation maps (llmInputs, llmOutputsPendingInput, modelCallsByCallId, modelTimingsByLlmKey) refactored to filter by sessionOwnerKey; alias eviction simplified to remove by owner match; deleteSession removes by ownerKey.
LLM replay correlation via ownerKey
integrations/openclaw/src/hook-replay/llm.ts
All LLM input, output, and timing correlation switched from sessionKey/sessionId to sessionOwnerKey/ownerKey matching. Replay pairing, pending grace-timer resolution, and timing candidate selection now match record.sessionOwnerKey === session.ownerKey. observedAtMicros timestamps captured during lazy session creation for llm_input/llm_output handling and passed as timestamp option to ensureSession. Import wiring updated to use resolveSessionOwnerKey.
Tool lifecycle timestamp capture
integrations/openclaw/src/hook-replay/tool.ts
guardBeforeToolCall and replayAfterToolCall capture observedAtMicros and pass as timestamp to ensureSession. Blocked tool metadata emission includes timestamp field.
Backend lifecycle hooks and subagent lineage
integrations/openclaw/src/hooks-backend.ts
Lifecycle hooks (onSessionStart, onSessionEnd, onAgentEnd, onBeforeAgentFinalize) capture observedAtMicros and pass as timestamp to session creation. New PendingSubagentLineage tracking maps introduced to correlate parent/child session lineage by child session key and run id. onSubagentSpawned enhanced to track lineage and materialize deferred child roots. closeSession now materializes deferred roots and clears lineage before deletion. cleanupSession switched to resolveSessionOwnerKey lookup. emitSessionMark updated to accept optional timestampMicros parameter. New private helpers for resolving nested subagent root context, promoting/materializing deferred roots, and managing lineage lifecycle. Exported test helper resolveBackendSessionOwnerKey added.
Subagent lifecycle and deferred materialization tests
integrations/openclaw/test/hooks-backend.test.ts
Four new test cases: nesting child session activity under requester when subagent_spawned arrives first; reconciling deferred child when session_start arrives later; lazy-session materialization from only child lineage via onSubagentEnded; deferred model timing correlation with explicit session_start arriving after timing. Extended TestNemoRelayRuntime to record richer call details (timestamps, parent handles, scope roles). Added withMockedNow helper for deterministic time control.
Correlation test updates for ownerKey model
integrations/openclaw/test/llm-replay.test.ts
TTL eviction test mock records updated to use sessionOwnerKey instead of sessionKey to match backend record shape.
OpenClaw plugin documentation update
docs/supported-integrations/openclaw-plugin.mdx
Runtime Mapping table clarified to document that subagent_spawned/subagent_ended emit lifecycle marks and nest child session scopes under requester session when stable lineage is available.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • NVIDIA/NeMo-Relay#209: Also modifies session and LLM hook-replay codepaths to refine session context and provenance metadata behavior during correlation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title follows Conventional Commits format with correct type (feat), no scope, imperative summary, and is 54 characters—well under 72-character limit.
Description check ✅ Passed PR description includes all required template sections (Overview with confirmation checkboxes, Details, Where to start, Related Issues) with substantive technical content aligned to the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 89.47% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 4, 2026

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: 2

Caution

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

⚠️ Outside diff range comments (1)
integrations/openclaw/src/hook-replay/llm.ts (1)

1191-1193: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use ownerKey for internal replay bookkeeping fallback.

trajectoryRunKey() still falls back to session.sessionId, which this PR otherwise treats as aliasable/mutable. If hooks arrive without runId before/after a session-id remap, trajectoryReplayedRuns, agentRunInputSnapshots, and hookLlmOutputReplayCounts can split across two keys and cause duplicate replay or missed cleanup. Use the stable internal identity here instead.

Suggested fix
 function trajectoryRunKey(session: SessionState, runId?: string): string {
-  return runId ?? session.sessionId;
+  return runId ?? session.ownerKey;
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/openclaw/src/hook-replay/llm.ts` around lines 1191 - 1193,
trajectoryRunKey currently falls back to session.sessionId which is mutable;
change the fallback to the stable internal identity by returning runId ??
session.ownerKey (i.e., use SessionState.ownerKey) so replay bookkeeping like
trajectoryReplayedRuns, agentRunInputSnapshots, and hookLlmOutputReplayCounts
use the stable owner key instead of the sessionId.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@integrations/openclaw/src/hook-replay/tool.ts`:
- Around line 55-63: The session is being materialized with observedAtMicros,
which can be after the tool span start when the first event is after_tool_call;
change ensureSession(...) so its timestamp uses the derived tool start time
(compute startMicrosFromDuration(observedAtMicros, event.durationMs) or use an
existing derived variable) instead of observedAtMicros when event.type ===
'after_tool_call' (or when event.durationMs is present and start is earlier), so
materializeSessionRoot sees the correct backdated start; update the call site
around ensureSession in tool.ts to pass that computed start time and keep
observedAtMicros for other cases.

In `@integrations/openclaw/src/hooks-backend.ts`:
- Around line 86-87: The two maps pendingSubagentLineageByChildSessionKey and
pendingSubagentChildKeyByRunId can grow unbounded because entries are only
removed by forgetPendingSubagentLineage during closeSession; update the code to
evict or bound these maps: add TTL-based eviction or a max-size ring behavior
and remove expired/old entries from these maps inside evictExpiredReplayRecords
(and also call the remover from cleanupSession), and ensure subagent_spawned
handling records creation metadata (timestamp/runId) so
forgetPendingSubagentLineage, cleanupSession, or evictExpiredReplayRecords can
correlate and delete orphaned entries to prevent memory buildup.

---

Outside diff comments:
In `@integrations/openclaw/src/hook-replay/llm.ts`:
- Around line 1191-1193: trajectoryRunKey currently falls back to
session.sessionId which is mutable; change the fallback to the stable internal
identity by returning runId ?? session.ownerKey (i.e., use
SessionState.ownerKey) so replay bookkeeping like trajectoryReplayedRuns,
agentRunInputSnapshots, and hookLlmOutputReplayCounts use the stable owner key
instead of the sessionId.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 071b8d5d-ec90-427b-8982-ffe69035954b

📥 Commits

Reviewing files that changed from the base of the PR and between f7c9415 and 30650ad.

📒 Files selected for processing (7)
  • docs/supported-integrations/openclaw-plugin.mdx
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hook-replay/session.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/test/llm-replay.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Node.js / Test (linux-arm64)
  • GitHub Check: Node.js / Test (linux-amd64)
  • GitHub Check: Node.js / Test (windows-arm64)
  • GitHub Check: Node.js / Test (macos-arm64)
  • GitHub Check: Node.js / Test (windows-amd64)
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (17)
{docs/**,README.md,CONTRIBUTING.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

{docs/**,README.md,CONTRIBUTING.md}: For docs-only changes, run targeted checks only if commands, package names, or examples changed. Use just docs for docs-site builds and just docs-linkcheck when links changed
Run docs site build with just docs

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
{docs/**,README.md,CONTRIBUTING.md,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run docs link validation with just docs-linkcheck when links change

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
{docs/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify README and docs entry points still match current package names and paths for large or public-facing changes

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
{docs/**,examples/**,README.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Verify examples still run with documented commands for large or public-facing changes

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
{docs/**,README.md,**/Cargo.toml,**/package.json,**/*.md}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Ensure renamed public surfaces are reflected consistently in manifests and docs for large or public-facing changes

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
**/*.{md,mdx,py,sh,yaml,yml,toml,json}

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

Keep package names, repo references, and build commands current

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
**/*.mdx

📄 CodeRabbit inference engine (.agents/skills/contribute-docs/SKILL.md)

In MDX files, top-of-file comments must use JSX comment delimiters: {/* to open and */} to close. Do not use HTML comments for MDX SPDX headers.

MDX top-of-file SPDX comments must use {/* ... */} delimiters instead of HTML comment delimiters (Must-Fix)

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
**/*.{html,md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Include SPDX license header in HTML and Markdown files using HTML comment syntax

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
docs/**/*.{md,mdx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Update embedded documentation snippets, patch docs, and binding-support notes if examples or supported bindings changed

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
docs/**

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Run just docs or ./scripts/build-docs.sh html to regenerate ignored Fern API reference pages before validation for documentation site changes

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}

⚙️ CodeRabbit configuration file

{docs/**,README.md,CONTRIBUTING.md,RELEASING.md,SECURITY.md}: Review documentation for technical accuracy against the current API, command correctness, and consistency across language bindings.
Flag stale examples, missing SPDX headers where required, and instructions that no longer match CI or pre-commit behavior.

Files:

  • docs/supported-integrations/openclaw-plugin.mdx
**/*{test,spec,smoke}.{js,ts,py}

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Relevant integration tests or smoke path must pass

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/test/hooks-backend.test.ts
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/test/hooks-backend.test.ts
**/*.{wasm,js,ts}{,x}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure WebAssembly package naming conventions are consistent with generated package expectations and downstream consumption

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run Node.js formatting with npm run format --workspace=nemo-relay-node

Include SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
🧠 Learnings (7)
📚 Learning: 2026-05-21T22:48:57.484Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: .agents/skills/maintain-observability/SKILL.md:0-0
Timestamp: 2026-05-21T22:48:57.484Z
Learning: Applies to docs/{about/concepts/subscribers,export-observability-data/about}.md : Update documentation under `docs/about/concepts/subscribers.md` and `docs/export-observability-data/about.md` to reflect lifecycle changes: create, register, run, deregister, flush, shutdown

Applied to files:

  • docs/supported-integrations/openclaw-plugin.mdx
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • integrations/openclaw/test/llm-replay.test.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
📚 Learning: 2026-05-21T22:52:14.330Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: .agents/skills/validate-change/SKILL.md:0-0
Timestamp: 2026-05-21T22:52:14.330Z
Learning: For Node.js binding changes, use `test-node-binding`

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
📚 Learning: 2026-05-29T21:25:49.977Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-29T21:25:49.977Z
Learning: Applies to crates/node/**/*.{js,ts,tsx} : Node.js public entry points include the main runtime package plus `nemo-relay-node/typed`, `nemo-relay-node/plugin`, and `nemo-relay-node/adaptive`.

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/session.ts
📚 Learning: 2026-05-21T22:47:58.898Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: .agents/skills/add-middleware/SKILL.md:0-0
Timestamp: 2026-05-21T22:47:58.898Z
Learning: Applies to crates/core/src/api/runtime/state.rs : Add chain execution helpers to `NemoRelayContextState` following the pattern of existing methods like `tool_sanitize_request_chain` or `tool_request_intercepts_chain`

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
📚 Learning: 2026-05-26T21:03:12.012Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: .agents/skills/review-doc-style/SKILL.md:0-0
Timestamp: 2026-05-26T21:03:12.012Z
Learning: Keep documentation aligned with current NeMo Relay behavior, repo layout, and entry points

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
📚 Learning: 2026-05-29T21:25:49.977Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-05-29T21:25:49.977Z
Learning: Scope stacks are hierarchical and always have a root scope. They establish parent-child event relationships, visibility for scope-local middleware and subscribers, cleanup boundaries, and concurrent request isolation.

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
🔇 Additional comments (7)
docs/supported-integrations/openclaw-plugin.mdx (1)

271-271: LGTM!

integrations/openclaw/src/hook-replay/session.ts (3)

385-439: LGTM!


1-2: LGTM!

Also applies to: 44-67, 70-131, 453-477


177-204: ⚡ Quick win

Remove the requester-vs-child precedence collapse concern (call sites don’t pass both keys together)

resolveSessionOwnerKey iterates lookupSessionKeys with requesterSessionKey before childSessionKey, but the subagent handlers in integrations/openclaw/src/hooks-backend.ts call ensureSession in two separate attempts: first with requesterSessionKey only, then with childSessionKey only (no single input object contains both). As a result, the claimed “child collapses into requester” and rememberSessionAliases clobbering via the same input object won’t happen in the current flow; child nesting instead comes from resolveSessionRootContext setting parentHandle using resolveTrackedSession({ requesterSessionKey: ... }).

			> Likely an incorrect or invalid review comment.
integrations/openclaw/src/hook-replay/tool.ts (1)

25-32: LGTM!

Also applies to: 83-83

integrations/openclaw/src/hooks-backend.ts (1)

108-337: LGTM!

Also applies to: 345-357, 423-457, 492-631

integrations/openclaw/test/hooks-backend.test.ts (1)

391-684: LGTM!

Also applies to: 729-832, 852-861

Comment thread integrations/openclaw/src/hook-replay/tool.ts Outdated
Comment thread integrations/openclaw/src/hooks-backend.ts
Signed-off-by: mnajafian-nv <mnajafian@nvidia.com>
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.

Caution

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

⚠️ Outside diff range comments (1)
integrations/openclaw/src/hooks-backend.ts (1)

498-507: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Keep deferred child roots deferred until the requester root exists.

Line 507 correctly computes deferRootOpen: true when lineage is known but the requester root is still missing, but that defer state is broken a few frames later: onSubagentSpawned() still calls promoteDeferredSubagentSession(), and materializeDeferredSessionRoot() will open the child with parentHandle === undefined. In the subagent_spawned-before-parent-session_start ordering, the child becomes a top-level root and can never be nested under the requester afterward.

This needs to stay deferred until the parent root is available, and there should be a promotion path when the requester session eventually opens.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/openclaw/src/hooks-backend.ts` around lines 498 - 507, The
deferred child-root decision in resolvePendingSubagentLineage is correct but
later broken by onSubagentSpawned/promoteDeferredSubagentSession and
materializeDeferredSessionRoot which open a child as top-level when parentHandle
is undefined; change onSubagentSpawned to respect deferRootOpen (do not call
promoteDeferredSubagentSession if parentHandle is undefined), modify
materializeDeferredSessionRoot to no-op (keep deferred) when parentHandle is
undefined, and add logic in the session-start path (where
resolveTrackedSession/resolvePendingSubagentLineage are used) to detect when a
requester root becomes available and then call promoteDeferredSubagentSession to
materialize and attach any pending child sessions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@integrations/openclaw/src/hooks-backend.ts`:
- Around line 498-507: The deferred child-root decision in
resolvePendingSubagentLineage is correct but later broken by
onSubagentSpawned/promoteDeferredSubagentSession and
materializeDeferredSessionRoot which open a child as top-level when parentHandle
is undefined; change onSubagentSpawned to respect deferRootOpen (do not call
promoteDeferredSubagentSession if parentHandle is undefined), modify
materializeDeferredSessionRoot to no-op (keep deferred) when parentHandle is
undefined, and add logic in the session-start path (where
resolveTrackedSession/resolvePendingSubagentLineage are used) to detect when a
requester root becomes available and then call promoteDeferredSubagentSession to
materialize and attach any pending child sessions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: 7ade0fef-a945-444a-ac7a-7d7eeaec7840

📥 Commits

Reviewing files that changed from the base of the PR and between 30650ad and 15666b2.

📒 Files selected for processing (3)
  • integrations/openclaw/src/hook-replay/session.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Node.js / Test (macos-arm64)
  • GitHub Check: Node.js / Test (windows-amd64)
  • GitHub Check: Node.js / Test (linux-arm64)
  • GitHub Check: Node.js / Test (windows-arm64)
  • GitHub Check: Node.js / Test (linux-amd64)
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (6)
**/*{test,spec,smoke}.{js,ts,py}

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Relevant integration tests or smoke path must pass

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
**/*.{wasm,js,ts}{,x}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure WebAssembly package naming conventions are consistent with generated package expectations and downstream consumption

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run Node.js formatting with npm run format --workspace=nemo-relay-node

Include SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/src/hook-replay/session.ts
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/src/hook-replay/session.ts
🧠 Learnings (2)
📚 Learning: 2026-05-21T22:52:14.330Z
Learnt from: CR
Repo: NVIDIA/NeMo-Relay PR: 0
File: .agents/skills/validate-change/SKILL.md:0-0
Timestamp: 2026-05-21T22:52:14.330Z
Learning: For Node.js binding changes, use `test-node-binding`

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/src/hook-replay/session.ts
🔇 Additional comments (1)
integrations/openclaw/src/hook-replay/session.ts (1)

164-180: LGTM!

Also applies to: 245-245, 406-406

Signed-off-by: mnajafian-nv <mnajafian@nvidia.com>
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.

Caution

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

⚠️ Outside diff range comments (1)
integrations/openclaw/src/hooks-backend.ts (1)

262-297: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Don't promote the child root before the requester scope exists.

subagent_spawned still misparents spawn-first flows where no requester session has been seen yet. ensureSession({ requesterSessionKey }) cannot seed a new parent on its own because owner-key fallback only uses sessionId/sessionKey/childSessionKey/runId, so this path falls through to a deferred child session; promoteDeferredSubagentSession() then immediately materializes that child with parentHandle === undefined. If the requester session_start arrives later, the child is already locked in as a top-level scope. Please keep the child deferred until a requester root is available, or create a lazy requester session from requesterSessionKey first.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@integrations/openclaw/src/hooks-backend.ts` around lines 262 - 297,
onSubagentSpawned currently can materialize a child as top-level because
ensureSession({ requesterSessionKey }) doesn't create a requester root; change
the flow so you do not call
promoteDeferredSubagentSession(event.childSessionKey) until a requester session
exists or you explicitly create a lazy requester session: first check for an
existing requester session (via ensureSession with requesterSessionKey and
verifying it returned a true requester/root), if none and
ctx.requesterSessionKey is present then create a lazy requester root (call
ensureSession in a mode that seeds a requester root) or otherwise keep the child
deferred; only call promoteDeferredSubagentSession when the requester root is
present. Ensure to reference onSubagentSpawned, ensureSession, and
promoteDeferredSubagentSession when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@integrations/openclaw/src/hooks-backend.ts`:
- Around line 262-297: onSubagentSpawned currently can materialize a child as
top-level because ensureSession({ requesterSessionKey }) doesn't create a
requester root; change the flow so you do not call
promoteDeferredSubagentSession(event.childSessionKey) until a requester session
exists or you explicitly create a lazy requester session: first check for an
existing requester session (via ensureSession with requesterSessionKey and
verifying it returned a true requester/root), if none and
ctx.requesterSessionKey is present then create a lazy requester root (call
ensureSession in a mode that seeds a requester root) or otherwise keep the child
deferred; only call promoteDeferredSubagentSession when the requester root is
present. Ensure to reference onSubagentSpawned, ensureSession, and
promoteDeferredSubagentSession when making this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Enterprise

Run ID: dd2eb2ce-6cb7-40a3-8fd3-cdf1e6977ce4

📥 Commits

Reviewing files that changed from the base of the PR and between 15666b2 and 997f9ad.

📒 Files selected for processing (4)
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/src/hooks-backend.ts
  • integrations/openclaw/test/hooks-backend.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Preview docs
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{wasm,js,ts}{,x}

📄 CodeRabbit inference engine (.agents/skills/maintain-packaging/SKILL.md)

Ensure WebAssembly package naming conventions are consistent with generated package expectations and downstream consumption

Files:

  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (.agents/skills/validate-change/SKILL.md)

Run Node.js formatting with npm run format --workspace=nemo-relay-node

Include SPDX license header in all JavaScript and TypeScript source files using double-slash comment syntax

Files:

  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
**/*.{rs,py,js,ts,tsx,jsx,go,sh,toml,yaml,yml,md}

📄 CodeRabbit inference engine (AGENTS.md)

Keep SPDX headers on source, docs, scripts, and configuration files. The project is Apache-2.0.

Files:

  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
**/*.{rs,py,go,js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Follow binding naming conventions: Rust and Python use snake_case, C FFI exports prefixed nemo_relay_, Go uses PascalCase for public APIs, Node.js uses camelCase.

Files:

  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
**/*{test,spec,smoke}.{js,ts,py}

📄 CodeRabbit inference engine (.agents/skills/contribute-integration/SKILL.md)

Relevant integration tests or smoke path must pass

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
{crates/adaptive/**/*.rs,**/*test*.{rs,py,go,ts,js},**/*adaptive*test*.{rs,py,go,ts,js},docs/plugins/adaptive/**}

📄 CodeRabbit inference engine (.agents/skills/maintain-optimizer/SKILL.md)

Maintain documented and tested validation and report behavior for adaptive surfaces

Files:

  • integrations/openclaw/test/hooks-backend.test.ts
🧠 Learnings (1)
📚 Learning: 2026-05-07T18:04:44.387Z
Learnt from: mnajafian-nv
Repo: NVIDIA/NeMo-Flow PR: 67
File: integrations/openclaw/src/modules.ts:1-2
Timestamp: 2026-05-07T18:04:44.387Z
Learning: In NVIDIA/NeMo-Flow, TypeScript source files should use `//` line comments for SPDX headers (e.g., `// SPDX-FileCopyrightText: ...` and `// SPDX-License-Identifier: ...`) rather than C-style block comments (`/* ... */`). The repo’s copyright checker enforces this mapping, so `//` SPDX headers in `.ts` files should not be flagged as a style violation.

Applied to files:

  • integrations/openclaw/src/hook-replay/tool.ts
  • integrations/openclaw/test/hooks-backend.test.ts
  • integrations/openclaw/src/hook-replay/llm.ts
  • integrations/openclaw/src/hooks-backend.ts
🔇 Additional comments (2)
integrations/openclaw/src/hook-replay/llm.ts (1)

1191-1193: LGTM!

integrations/openclaw/src/hook-replay/tool.ts (1)

55-57: LGTM!

Also applies to: 63-63, 84-84

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Feature a new feature lang:js PR changes/introduces Javascript/Typescript code size:L PR is large

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant