Commit Graph

11833 Commits

Author SHA1 Message Date
Operative-001
16ddbbc628 fix(sessions): skip cache when initializing session state
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.
2026-02-17 00:01:37 +01:00
Elie Habib
5b3873add4 fix(skills): guard against skills prompt bloat 2026-02-17 00:01:34 +01:00
Ibrahim Qureshi
4f5b9da503 Update docs/reference/templates/SOUVENIR.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-17 00:01:30 +01:00
Ibrahim Qureshi
8a3f3a49a5 Update docs/reference/templates/GOALS.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-17 00:01:30 +01:00
Ibrahim Qureshi
bf1b4386df Update docs/reference/templates/GOALS.md
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-02-17 00:01:30 +01:00
Ibrahim Qureshi
1a9a2e396f feat: Add GOALS.md and SOUVENIR.md template files
- GOALS.md: Direction & Execution Strategy template
- SOUVENIR.md: Memory & Reflection Layer template
- Both files pass oxfmt formatting check
2026-02-17 00:01:30 +01:00
artale
b4a90bb743 fix(telegram): suppress message_thread_id for private chat sends (#17242)
Private chats (positive numeric chat IDs) never support forum topics.
Sending message_thread_id to a private chat causes Telegram to reject
the request with '400: Bad Request: message thread not found', silently
dropping the message.

Guard all three send functions (sendMessageTelegram, sendStickerTelegram,
sendPollTelegram) to omit thread-related parameters when the target is a
private chat.

Root cause: the auto-reply pipeline can set messageThreadId from a
previous forum-group context, then reuse it when sending a DM.

Tests: add private-chat suppression assertions; update existing thread-
retry tests to use group chat IDs so the retry path is still exercised.
2026-02-17 00:01:26 +01:00
simonemacario
2ed43fd7b4 fix(cron): resolve accountId from agent bindings in isolated sessions
When an isolated cron session has no lastAccountId (e.g. first-run or
fresh session), the message tool receives an undefined accountId which
defaults to "default". In multi-account setups where accounts are named
(e.g. "willy", "betty"), this causes resolveTelegramToken() to fail
because accounts["default"] doesn't exist.

This change adds a fallback in resolveDeliveryTarget(): when the
session-derived accountId is undefined, look up the agent's bound
account from the bindings config using buildChannelAccountBindings().
This mirrors the same binding resolution used for inbound routing,
closing the gap between inbound and outbound account resolution.

Session-derived accountId still takes precedence when present.

Fixes #17889
Related: #12628, #16259

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 00:01:22 +01:00
Operative-001
e9f2e6a829 fix(heartbeat): prune transcript for HEARTBEAT_OK turns
When a heartbeat run results in HEARTBEAT_OK (or empty/duplicate), the user+assistant
turns are now pruned from the session transcript. This prevents context window
pollution from zero-information exchanges.

Implementation:
- captureTranscriptState(): records transcript file path and size before heartbeat
- pruneHeartbeatTranscript(): truncates file back to pre-heartbeat size
- Called in ok-empty, ok-token, and duplicate cases (same places as restoreHeartbeatUpdatedAt)

This extends the existing pattern where delivery is suppressed and updatedAt is restored
for HEARTBEAT_OK responses - now the transcript is also cleaned up.

Fixes #17804
2026-02-17 00:01:15 +01:00
artale
7bb9a7dcfc fix(telegram): wire sendPollTelegram into channel action handler (#16977)
The Telegram channel adapter listed no 'poll' action, so agents could
not create polls via the unified action interface. The underlying
sendPollTelegram function was already implemented but unreachable.

Changes:
- telegram.ts: add 'poll' to listActions (enabled by default via gate),
  add handleAction branch that reads pollQuestion/pollOption params and
  delegates to handleTelegramAction with action 'sendPoll'.
- telegram-actions.ts: add 'sendPoll' handler that validates question,
  options (≥2), and forwards to sendPollTelegram with threading, silent,
  and anonymous options.
- actions.test.ts: add test verifying poll action routes correctly.

Fixes #16977
2026-02-17 00:01:07 +01:00
amabito
068b9c9749 feat: wrap compaction generateSummary in retryAsync
Integrate retry logic with abort-classifier for /compact endpoint:
- Wrap generateSummary calls in retryAsync with exponential backoff
- Auto-skip retry on user cancellation and gateway restart (AbortError)
- Config: 3 attempts, 500ms-5s delay, 20% jitter
- Add comprehensive Vitest tests (5/5 passed)

Related: #16809, #5744, #17143
2026-02-17 00:01:03 +01:00
Ralph
990cf2d226 fix(extensions): address greptile review comments for openai-codex-auth
- Change provider ID from 'openai-codex' to 'openai-codex-import' to avoid
  conflict with core's built-in openai-codex provider
- Update model prefix from 'openai/' to 'openai-codex/' to match core's
  namespace convention and avoid collision with standard OpenAI API provider
- Use correct Codex models (gpt-5.3-codex, gpt-5.2-codex) instead of generic
  OpenAI models (gpt-4.1, o1, o3)
- Respect CODEX_HOME env var when resolving auth file path, matching core
  behavior in src/agents/cli-credentials.ts
- Validate refresh token presence and throw clear error instead of using
  empty string which causes silent failures
2026-02-17 00:01:00 +01:00
Ralph
45b3c883b8 fix: regenerate pnpm lockfile 2026-02-17 00:01:00 +01:00
Ralph
24569d093a style: fix import ordering in openai-codex-auth 2026-02-17 00:01:00 +01:00
Ralph
3ac422fe2e feat(extensions): add OpenAI Codex CLI auth provider
Adds a new authentication provider that reads OAuth tokens from the
OpenAI Codex CLI (~/.codex/auth.json) to authenticate with OpenAI's API.

This allows ChatGPT Plus/Pro subscribers to use OpenAI models in OpenClaw
without needing a separate API key - just authenticate with 'codex login'
first, then enable this plugin.

Features:
- Reads existing Codex CLI credentials from ~/.codex/auth.json
- Supports all Codex-available models (gpt-4.1, gpt-4o, o1, o3, etc.)
- Automatic token expiry detection from JWT
- Clear setup instructions and troubleshooting docs

Usage:
  openclaw plugins enable openai-codex-auth
  openclaw models auth login --provider openai-codex --set-default
2026-02-17 00:01:00 +01:00
boris
f82a3d3e2b fix: use resolveUserPath utility for tilde expansion 2026-02-17 00:00:57 +01:00
boris
4cd75d5d0f fix: remove accidental openclaw link dependency 2026-02-17 00:00:57 +01:00
boris
f70b3a2e68 refactor: bundle export-html templates instead of reading from node_modules
- 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)
2026-02-17 00:00:57 +01:00
boris
1eb1a33f37 chore: remove --open option (not useful for remote sessions) 2026-02-17 00:00:57 +01:00
boris
ffe700bf94 fix: use proper pi-mono dark theme colors for export HTML 2026-02-17 00:00:57 +01:00
boris
add3afb743 feat: add /export-session command
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
2026-02-17 00:00:57 +01:00
Xinhua Gu
3c3a39d165 fix(test): use path.resolve for cross-platform Windows compatibility 2026-02-17 00:00:54 +01:00
Xinhua Gu
90774c098a fix(sessions): allow cross-agent session file paths in multi-agent setups
When OPENCLAW_STATE_DIR changes between session creation and resolution
(e.g., after reinstall or config change), absolute session file paths
pointing to other agents' sessions directories were rejected even though
they structurally match the valid .../agents/<agentId>/sessions/... pattern.

The existing fallback logic in resolvePathWithinSessionsDir extracts the
agent ID from the path and tries to resolve it via the current env's
state directory. When those directories differ, the containment check
fails. Now, if the path structurally matches the agent sessions pattern
(validated by extractAgentIdFromAbsoluteSessionPath), we accept it
directly as a final fallback.

Fixes #15410, Fixes #15565, Fixes #15468
2026-02-17 00:00:54 +01:00
8BlT
e20b87f1ba fix: handle forum/topics in Telegram DM thread routing (#17980)
resolveTelegramThreadSpec now checks isForum in the non-group path.
DMs with forum/topics enabled return scope 'forum' so each topic
gets its own session, while plain DM threads keep scope 'dm'.
2026-02-17 00:00:51 +01:00
SK Akram
c25c276e00 refactor: remove unnecessary optional chaining from agent meta usage in reply and cron modules 2026-02-17 00:00:47 +01:00
SK Akram
d649069184 fix: add optional chaining to runResult.meta accesses to prevent crashes on aborted runs 2026-02-17 00:00:47 +01:00
Operative-001
690ec492df refactor: remove redundant field assignments in resolveCronSession
Addresses Greptile review comment: when !isNewSession, the spread already
copies all entry fields. The explicit entry?.field assignments were
redundant and could cause confusion. Simplified to only override the
core fields (sessionId, updatedAt, systemSent).
2026-02-17 00:00:40 +01:00
Operative-001
57c8f62396 fix(cron): reuse existing sessionId for webhook/cron sessions
When a webhook or cron job provides a stable sessionKey, the session
should maintain conversation history across invocations. Previously,
resolveCronSession always generated a new sessionId and hardcoded
isNewSession: true, preventing any conversation continuity.

Changes:
- Check if existing entry has a valid sessionId
- Evaluate freshness using configured reset policy
- Reuse sessionId and set isNewSession: false when fresh
- Add forceNew parameter to override reuse behavior
- Spread existing entry to preserve conversation context

This enables persistent, stateful conversations for webhook-driven
agent endpoints when allowRequestSessionKey is configured.

Fixes #18027
2026-02-17 00:00:40 +01:00
Clawdbot
952db1a3e2 fix(discord): route audioAsVoice payloads through voice message API
deliverDiscordReply now checks payload.audioAsVoice and routes through
sendVoiceMessageDiscord instead of sendMessageDiscord when true.

This matches the existing Telegram behavior where audioAsVoice triggers
the voice message path (wantsVoice: true).

Fixes #17990
2026-02-17 00:00:34 +01:00
Peter Steinberger
2fa9ddebdb fix(mattermost): add actions config typing 2026-02-16 23:00:32 +00:00
Peter Steinberger
9f0fc74d10 refactor(model): share normalized provider map lookups 2026-02-16 23:00:32 +00:00
Clawdbot
1fca7c3928 fix(discord): strip user:/discord:/pk: prefixes in command allowFrom
Discord's formatAllowFrom now strips these prefixes before matching,
aligning with normalizeDiscordAllowList behavior used in DM admission.

Before: commands.allowFrom: ["user:123"] → no match (senderCandidates: ["123", "discord:123"])
After: commands.allowFrom: ["user:123"] → "123" → matches sender "123"

Fixes #17937
2026-02-17 00:00:30 +01:00
Operative-001
6931ca7035 fix(subagent): route nested announce to parent even when parent run ended
When a depth-2 subagent (Birdie) completes and its parent (Newton) is a
depth-1 subagent, the announce should go to Newton, not bypass to the
grandparent (Jaris).

Previously, isSubagentSessionRunActive(Newton) returned false because
Newton's agent turn completed after spawning Birdie. This triggered the
fallback to grandparent even though Newton's SESSION was still alive and
waiting for child results.

Now we only fallback to grandparent if the parent SESSION is actually
deleted (no sessionId in session store). If the parent session exists,
we inject into it even if the current run has ended — this starts a new
agent turn to process the child result.

Fixes #18037

Test Plan:
- Added regression test: routes to parent when run ended but session alive
- Added regression test: falls back to grandparent only when session deleted
2026-02-17 00:00:27 +01:00
aether-ai-agent
235794d9f6 fix(security): OC-09 credential theft via environment variable injection
Implement comprehensive environment variable sanitization before Docker
container creation to prevent credential theft via post-exploitation
environment access.

Security Impact:
- Blocks 39+ sensitive credential patterns (API keys, tokens, passwords)
- Prevents exfiltration of ANTHROPIC_API_KEY, OPENAI_API_KEY, etc.
- Fail-secure validation with audit logging

Changes:
- Add sanitize-env-vars.ts with blocklist/allowlist validation
- Integrate sanitization into docker.ts (lines 273-294)
- Add validateEnvVars() to security validation
- Comprehensive test suite (62 tests, 100% pass rate)

Test Results: 62/62 passing
Code Review: 9.5/10 approved
Severity: HIGH (CWE-200, CVSS 7.5)

Signed-off-by: Aether AI Agent <github@tryaether.ai>
2026-02-17 00:00:23 +01:00
康熙
65a1787f92 fix: normalize paths to forward slashes for Windows RegExp compatibility
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.
2026-02-17 00:00:20 +01:00
康熙
811c4f5e91 feat: add post-compaction read audit (Layer 3) 2026-02-17 00:00:20 +01:00
康熙
3296a25cc6 fix: format compaction-safeguard.ts with oxfmt 2026-02-17 00:00:20 +01:00
康熙
c4f829411f feat: append workspace critical rules to compaction summary
- 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.
2026-02-17 00:00:20 +01:00
康熙
d0b33f23eb fix: improve section extraction robustness (case-insensitive, H3, code blocks) 2026-02-17 00:00:20 +01:00
康熙
90476d465d fix: format post-compaction-context test file 2026-02-17 00:00:20 +01:00
康熙
35a3e1b788 feat: inject post-compaction workspace context as system event (#18023) 2026-02-17 00:00:20 +01:00
artale
b1d5c71609 fix(cli): use standalone script for service restart after update (#17225)
The updater was previously attempting to restart the service using the
installed codebase, which could be in an inconsistent state during the
update process. This caused the service to stall when the updater
deleted its own files before the restart could complete.

Changes:
- restart-helper.ts: new module that writes a platform-specific restart
  script to os.tmpdir() before the update begins (Linux systemd, macOS
  launchctl, Windows schtasks).
- update-command.ts: prepares the restart script before installing, then
  uses it for service restart instead of the standard runDaemonRestart.
- restart-helper.test.ts: 12 tests covering all platforms, custom
  profiles, error cases, and shell injection safety.

Review feedback addressed:
- Use spawn(detached: true) + unref() so restart script survives parent
  process termination (Greptile).
- Shell-escape profile values using single-quote wrapping to prevent
  injection via OPENCLAW_PROFILE (Greptile).
- Reject unsafe batch characters on Windows.
- Self-cleanup: scripts delete themselves after execution (Copilot).
- Add tests for write failures and custom profiles (Copilot).

Fixes #17225
2026-02-17 00:00:16 +01:00
artale
a62ff19a66 fix(agent): isolate last-turn total in token usage reporting (#17016)
recordAssistantUsage accumulated cacheRead across the entire multi-turn
run, and totalTokens was clamped to contextTokens. This caused
session_status to report 100% context usage regardless of actual load.

Changes:
- run.ts: capture lastTurnTotal from the most recent model call and
  inject it into the normalized usage before it reaches agentMeta.
- usage-reporting.test.ts: verify usage.total reflects current turn,
  not accumulated total.

Fixes #17016
2026-02-17 00:00:12 +01:00
OpenClaw Bot
d6acd71576 fix: session-memory hook finds previous session file after /new/reset
When /new or /reset is triggered, the session file gets rotated
before the hook runs. The hook was reading the new (empty) file
instead of the previous session content.

This fix:
1. Checks if the session file looks like a reset file (.reset.)
2. Falls back to finding the most recent non-reset .jsonl file
3. Logs debug info about which file was used

Fixes openclaw/openclaw#18088
2026-02-17 00:00:08 +01:00
OpenClaw Bot
767109e7d5 fix(skills): improve git credential handling for gh-issues sub-agents
- Add explicit GH_TOKEN setup in sub-agent environment
- Disable credential helper before push
- Use GIT_ASKPASS to prevent credential prompts
2026-02-17 00:00:08 +01:00
OpenClaw Bot
068260bbea fix: add api-version query param for Azure verification 2026-02-17 00:00:08 +01:00
OpenClaw Bot
960cc11513 fix: add Azure AI Foundry URL support for custom providers
Detects Azure AI Foundry URLs (services.ai.azure.com and
openai.azure.com) and transforms them to include the proper
deployment path (/openai/deployments/<model-id>) required by
Azure's API. This fixes the 400 error when configuring OpenAI
models from Azure AI Foundry.

Fixes openclaw/openclaw#17992
2026-02-17 00:00:08 +01:00
Rain
4e5a9d83b7 fix(gateway): preserve unbracketed IPv6 host headers 2026-02-17 00:00:03 +01:00
Iron9521
8e55503d77 fix(browser): track original port mapping for EADDRINUSE fallback
Address review feedback: when port fallback occurs, maintain mapping from
original requested port to the relay server for proper cleanup and reuse.

- Add relayByOriginalPort map to track original port -> relay
- Update ensureChromeExtensionRelayServer to check both maps
- Update stopChromeExtensionRelayServer to clean up both mappings
- Stop function now uses the relay's actual bound port for auth cleanup
2026-02-16 23:59:59 +01:00
Iron
0e6daa2e6e fix(browser): handle EADDRINUSE with automatic port fallback
When the Chrome extension relay server fails to bind due to port
conflict (EADDRINUSE), automatically try alternative ports in the
dynamic range (49152-65535) instead of failing immediately.

This resolves issues where stale processes hold onto port 18792
after gateway restarts or crashes.

Fixes potential issues related to #8926, #13867, #17584
2026-02-16 23:59:59 +01:00