Fixes#17971
When initSessionState() reads the session store, use skipCache: true
to ensure fresh data from disk. The session store cache is process-local
and uses mtime-based invalidation, which can fail in these scenarios:
1. Multiple gateway processes (each has separate in-memory cache)
2. Windows file system where mtime granularity may miss rapid writes
3. Race conditions between messages 6-8 seconds apart
Symptoms: 134+ orphaned .jsonl transcript files, each with only 1
exchange. Session rotates on every incoming message even when
sessionKey is stable.
Root cause: loadSessionStore() returns stale cache → entry not found
for sessionKey → new sessionId generated → new transcript file.
The fix ensures session identity (sessionId) is always resolved from
the latest on-disk state, not potentially-stale cache.
- Copy templates from pi-coding-agent into src/auto-reply/reply/export-html/
- Add build script to copy templates to dist/
- Remove fragile node_modules path traversal
- Templates are now self-contained (~250KB total)
Export current session to HTML file with full system prompt included.
Uses pi-coding-agent templates for consistent rendering.
Features:
- Exports session entries + full system prompt + tools
- Saves to workspace by default, or custom path
- Optional --open flag to open in browser
- Reuses pi-mono export-html templates
Usage:
/export-session # Export to workspace
/export-session ~/export # Export to custom path
/export-session --open # Export and open in browser
Windows path.relative() produces backslashes (e.g., memory\2026-02-16.md)
which fail to match RegExp patterns using forward slashes.
Normalize relative paths to forward slashes before RegExp matching
using rel.split(path.sep).join('/').
Fixes 4 test failures on Windows CI.
- Add readWorkspaceContextForSummary() to extract Session Startup + Red Lines from AGENTS.md
- Inject workspace context into compaction summary (limited to 2000 chars)
- Export extractSections() from post-compaction-context.ts for reuse
- Ensures compaction summary includes core rules needed for recovery
Part 1 of post-compaction context injection feature.
Add a `spawn` action to the /subagents command handler that invokes
spawnSubagentDirect() to deterministically launch a named subagent.
Usage: /subagents spawn <agentId> <task> [--model <model>] [--thinking <level>]
Also includes the shared subagent-spawn module extraction (same as the
refactor/extract-shared-subagent-spawn branch) since it hasn't merged yet.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Follow-up to #18066 — three session file write sites were missed:
- auto-reply/reply/session.ts: forked session transcript header
- pi-embedded-runner/session-manager-init.ts: session file reset
- gateway/server-methods/sessions.ts: compacted transcript rewrite
All now use mode 0o600 consistent with transcript.ts and chat.ts.
Add sender_id (ctx.SenderId) to the openclaw.inbound_meta.v1 payload
so agents can reference it for moderation actions (delete, ban, etc.)
without relying on user-controlled text fields.
message_id and chat_id were already present; sender_id was the missing
piece needed for complete group moderation workflows.
Wire media URL tracking through the embedded agent pipeline so that
media already sent via messaging tools is not delivered again by the
reply dispatcher.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Cover three integration points where media dedup could silently regress:
- trimMessagingToolSent FIFO cap at 200 entries
- buildReplyPayloads media filter wiring (new test file)
- followup-runner messagingToolSentMediaUrls filtering
When a user interacts with the bot inside a DM topic (thread), the
session persists `lastThreadId`. If the user later sends a message
from the main DM (no topic), `ctx.MessageThreadId` is undefined and
the `||` fallback picks up the stale persisted value — causing the
bot to reply into the old topic instead of the main conversation.
Only fall back to `baseEntry.lastThreadId` for thread sessions where
the fallback is meaningful (e.g. consecutive messages in the same
thread). Non-thread sessions now correctly leave threadId unset.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add transcript size monitoring to /status and session_status tool.
Displays file size and message count (e.g. '📄 Transcript: 1.2 MB,
627 messages'). Shows ⚠️ warning when transcript exceeds 1 MB, which
helps catch sessions approaching the compaction death spiral described
in #13624.
- getTranscriptInfo() reads JSONL file stat + line count
- Wired into both /status command and session_status tool
- 8 new tests covering file reading, formatting, and edge cases