fix(sessions): classify spawn-child sessions correctly; extract shared classifier#79544
Conversation
|
Codex review: needs maintainer review before merge. Summary Reproducibility: yes. source-reproducible with high confidence: current main passes session entries into duplicated classifiers that ignore Real behavior proof Next step before merge Security Review detailsBest possible solution: Merge the shared classifier after normal maintainer and CI checks; keep gateway session-row kind parity as a separate follow-up if maintainers want that API widened. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible with high confidence: current main passes session entries into duplicated classifiers that ignore Is this the best way to solve the issue? Yes. Checking existing What I checked:
Likely related people:
Codex review notes: model gpt-5.5, reasoning high; reviewed against 8046b5e4621b. |
Live behavior repro (deployed container)Container: Underlying store DOES carry
|
|
Maintainer-collected after-fix behavior proof for #79544. Provider: Azure Crabbox Exact command surface exercised after this patch: pnpm test src/commands/sessions.kind-classification.test.ts
pnpm build
pnpm openclaw sessions --all-agents --json # against a seeded ACP spawn-child session store
pnpm openclaw status --json # against the same seeded store
node scripts/run-oxlint.mjs src/sessions/classify-session-kind.ts src/commands/sessions.ts src/commands/status.summary.runtime.ts src/commands/status.types.ts src/commands/sessions.kind-classification.test.tsAfter-fix proof lines: Verification results: Proof scope: this is after-fix CLI/status behavior from a seeded session store on the PR branch. It is not live Telegram/Copilot ACP auth proof. |
…n-child sessions (catalog openclaw#19)
…d classifier; closes catalog openclaw#19
… type The PR added "spawn-child" to SessionKind in classify-session-kind.ts but status.types.ts still had a hardcoded literal union that omitted it, causing TS2322 across all typecheck + build CI shards.
996e785 to
6b5294c
Compare
|
Landing verification for PR #79544 at head Behavior addressed: ACP spawn-child sessions with persisted Real environment tested: Azure Crabbox Linux lease Exact steps or command run after this patch: pnpm test src/commands/sessions.kind-classification.test.ts
pnpm build
pnpm openclaw sessions --all-agents --json
pnpm openclaw status --json
node scripts/run-oxlint.mjs src/sessions/classify-session-kind.ts src/commands/sessions.ts src/commands/status.summary.runtime.ts src/commands/status.types.ts src/commands/sessions.kind-classification.test.tsAdditional local verification after rebase/test-fix on head pnpm test src/commands/sessions.kind-classification.test.ts src/commands/status.summary.runtime.test.ts
OPENCLAW_VITEST_INCLUDE_FILE=<generated agentic-commands-status-tools file> OPENCLAW_VITEST_SHARD_NAME=agentic-commands-status-tools OPENCLAW_TEST_PROJECTS_PARALLEL=2 OPENCLAW_VITEST_MAX_WORKERS=2 NODE_OPTIONS=--max-old-space-size=8192 pnpm exec node scripts/test-projects.mjs test/vitest/vitest.commands.config.ts
node scripts/run-oxlint.mjs src/sessions/classify-session-kind.ts src/commands/sessions.ts src/commands/status.summary.runtime.ts src/commands/status.types.ts src/commands/sessions.kind-classification.test.ts src/commands/sessions.test.tsEvidence after fix: Observed result after fix: CI/Testbox run IDs checked before landing:
What was not tested: live Telegram/Copilot ACP auth was not exercised in this maintainer proof; the after-fix behavior proof used a seeded session store that mirrors the deployed store shape from the before-fix repro. |
Summary
ACP spawn-child sessions (Telegram supergroup-spawned children, etc.) were misclassified as
kind: "direct"inopenclaw sessionslistings. Add a new"spawn-child"kind, extract the duplicated session classifier into a shared helper, and make the new kind authoritative whenentry.spawnedByis set. Closes catalog #19.Bug detail
ACP sessions with
spawnedBy: "agent:main:telegram:group:-1003967207344:topic:1"and a populateddeliveryContextwere reported askind: "direct"inopenclaw sessions --json. Most ACP sessions in deployed listings showeddirecteven though they had been spawned from a group with a bound thread context. Operators relying on thekindfield for dashboards / triage filters were missing spawn-child sessions entirely.How to reproduce
Live repro before fix:
```bash
docker compose exec codeclaw-openclaw openclaw sessions --all-agents --json
| jq '.sessions[] | select(.key | contains("acp:")) | {key, kind, deliveryContext}'
Before: ACP sessions with spawnedBy still show "kind": "direct"
After: sessions with spawnedBy show "kind": "spawn-child"
```
Unit-level:
```bash
git checkout 8e48d27 # red-test cherry-pick
pnpm test src/commands/sessions.kind-classification.test.ts
Before fix: 2 RED + 2 GREEN controls
After fix commits a48d6d5 + d89ec0c: all 4 GREEN
```
Root cause
classifySessionKeywas implemented twice — atsrc/commands/sessions.ts:136-152and duplicated atsrc/commands/status.summary.runtime.ts:129-145. Both classified by session-key shape only, not by entry metadata. ACP-style keys carry no embedded channel/thread indicator, so spawn-child ACP entries fell through to"direct". ThespawnedByfield on the entry was never consulted.Fix approach
src/sessions/classify-session-kind.ts(classifySessionKind). Existing logic preserved.entry?.spawnedBytruthy check that returns"spawn-child"BEFORE the key-shape branch — so spawn-child ACP sessions take precedence over the (now-correct but no-longer-default) shape-based fallback."spawn-child"to theSessionKindunion and toSessionRow["kind"]insrc/commands/sessions.ts.src/commands/status.summary.runtime.tswith the shared import. The existing publicclassifySessionKeyAPI surface is preserved via an alias onstatusSummaryRuntime, so the existing test instatus.summary.runtime.test.tsand the consumer instatus.summary.tsneed no changes.KIND_PADconstant insessions.tsfrom 6 to 11 to fit the new longest label and keep the table column aligned.Tests
src/commands/sessions.kind-classification.test.ts— 4 cases (2 RED → GREEN, 2 GREEN controls stay GREEN).src/commands/— 196 files / 1502 tests pass.Catalog reference
Catalog finding: #19. Investigation lives in branch
edpiva/acp-native-session-investigation(not yet upstream) atdocs/plans/2026-05-08-acp-findings-catalog.md.Verification commands
```bash
pnpm test src/commands/sessions.kind-classification.test.ts # 4/4 GREEN
pnpm test src/commands/ # 1502/0
```
Notes for reviewers
src/gateway/session-utils.ts:858for the gateway RPC surface (GatewaySessionRow["kind"]="direct" | "group" | "global" | "unknown"— narrower contract). It is intentionally NOT touched by this PR; if the gateway listing surface should also expose"spawn-child", that's a separate follow-up.SessionKindis intentionally a different name than the existingSessionKindinsrc/agents/tools/sessions-helpers.ts— different domains, different unions, no shared imports. They co-exist safely.formatKindCellhas a fallthrough that handles"spawn-child"by displaying it muted. The explicit"spawn-child"branch was removed because it was identical to the fallthrough.Live behavior repro (deployed container)
Container:
codeclaw-openclaw:cleanrunning OpenClaw 2026.5.6 (e9d4a0a)PII note: Telegram chat/group/topic IDs redacted to
<chat-id>/<topic-id>.Underlying store DOES carry
spawnedBy, butopenclaw sessions --jsonprojects it away → kind defaults to"direct"The disk-side store at
~/.openclaw/agents/copilot/sessions/sessions.jsonrecords the spawning context for every spawn-child entry:But the same sessions in
openclaw sessions --all-agents --jsonreportspawnedBy: nullandkind: "direct"(or"group"for some) — the underlying signal isn't reaching the row classifier:{"key":"agent:copilot:acp:86b7b5af-…","kind":"direct","spawnedBy":null,"deliveryContext":null,…} {"key":"agent:copilot:acp:f5ab8dba-…","kind":"direct","spawnedBy":null,"deliveryContext":null,…} {"key":"agent:copilot:acp:d6fb7506-…","kind":"group","spawnedBy":null,"deliveryContext":null,…}After this PR,
classifySessionKindchecksentry?.spawnedBytruthy BEFORE the key-shape branch, so spawn-child rows resolve tokind: "spawn-child"and operators can filter dashboards correctly.(The follow-up gap noted by the reviewer —
src/gateway/session-utils.tsstill uses the narrowerGatewaySessionRow["kind"]enum without"spawn-child"— is intentionally out of scope for this PR; tracked for a follow-up.)Updates
2026-05-08 (follow-up commit
14c84f2bf8): Fixed post-push CI regression.src/commands/status.types.tshadSessionStatus.kindtyped as a hardcoded literal union"cron" | "direct" | "group" | "global" | "unknown"— the PR's new"spawn-child"member was not included, causingTS2322across all type-check and build shards (check-prod-types,check-test-types,check-lint,build-artifacts,check-strict-smoke,check-additional-extension-*,build-smoke). Fix: import and use the canonicalSessionKindtype fromsrc/sessions/classify-session-kind.tsso the union stays in sync automatically. Thesecurity-dependency-auditfailure (fast-uriCVE GHSA-v39h-62p7-jpjc) is pre-existing onmain(green on commitac5acaeb) and unrelated to this PR.Real behavior proof
spawnedBymetadata classify askind: "spawn-child"instead of falling through to"direct"inopenclaw sessionsand status output.brisk-barnacle/cbx_5c3278f7e9f2, fresh checkout of PR head996e78598f958efb2e86ab381eda6c08eefdd8cc, Node 22.22.2, seeded OpenClaw session store with ACP spawn-child and direct-control rows.openclaw sessions --all-agents --jsonreported the seeded ACP child row askind: "spawn-child"; the ACP direct-control row stayedkind: "direct";openclaw status --jsonalso reported the ACP child row askind: "spawn-child".