Skip to content

fix(server): honor Grok hideFromScrollback and render background tasks#2198

Open
jasonhnd wants to merge 10 commits into
getpaseo:mainfrom
jasonhnd:fix/2182-grok-hide-from-scrollback
Open

fix(server): honor Grok hideFromScrollback and render background tasks#2198
jasonhnd wants to merge 10 commits into
getpaseo:mainfrom
jasonhnd:fix/2182-grok-hide-from-scrollback

Conversation

@jasonhnd

Copy link
Copy Markdown
Contributor

Summary

  • Suppress Grok ACP user_message_chunk updates when _meta.hideFromScrollback === true (live stream and history replay), before message assembly so hidden chunks without messageId cannot contaminate later visible user messages.
  • Add narrow shared ACP hooks (shouldSuppressUserMessageChunk, extensionNotificationHandler) and route extension-driven timeline items through the same live-vs-history delivery path.
  • Parse Grok _x.ai/session/update task_backgrounded / task_completed at the adapter boundary and map them to synthetic tool_call items with a stable callId derived from task_id (completed / failed / canceled / missing output / truncated).
  • Keep Grok-specific logic in GrokACPAgentClient + grok-background-tasks.ts; do not introduce a global <system-reminder> text filter.
  • Update docs/providers.md for the Grok adapter and hook architecture.

Fixes #2182

Dependency

Depends on #2177 (Grok ACP adapter / Always Approve). This branch is stacked on the latest #2177 head (feat/2053-grok-always-approve-mode). Merge #2177 first, then rebase this PR onto main if needed.

Test plan

  • npx vitest run packages/server/src/server/agent/providers/grok-background-tasks.test.ts packages/server/src/server/agent/providers/grok-acp-agent.test.ts --bail=1 (34 passed)
  • npm run typecheck --workspace=@getpaseo/server
  • lint/format on touched files
  • Manual: Grok session with background shell task → no raw <system-reminder> user bubble; compact task/tool item for backgrounded → completed
  • Manual: non-zero exit / cancelled task / truncated output render distinct tool states
  • Manual: non-Grok ACP providers still show normal user chunks (including any _meta they may send)

jasonhnd added 4 commits July 18, 2026 11:16
Grok ACP does not advertise permission modes, so catalog sessions had no
Ask / Always Approve picker and blocked on every tool permission card.

Wire Paseo modes to Grok's documented controls: launch with
`grok agent --always-approve stdio` when Always Approve is selected, toggle
via `/always-approve on|off` mid-session, and keep a client-side
auto-approve fallback if Grok still emits request_permission.

Closes getpaseo#2053
GROK_MODES is now the single source for labels, icons, color tiers, and
isUnattended. The provider registry reuses that list instead of inlining a
duplicate. deriveModesFromACP re-attaches fallback metadata after ACP mode
derivation so Always Approve stays unattended for create-config inheritance.
Registry definition.modes requires required icon/colorTier fields.
Type the shared Grok mode list as AgentProviderModeDefinition[] so the
single-source list typechecks for both registry and session defaultModes.
Grok ACP marks model-only wake-up chunks with _meta.hideFromScrollback and
emits structured _x.ai/session/update task events. Suppress those chunks
before message assembly and map task_backgrounded/task_completed to stable
synthetic tool_call timeline items so long background work no longer leaks
as raw user bubbles.

Closes getpaseo#2182

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6922b81dca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +212 to +214
return {
handled: true,
currentModeId: GROK_ASK_MODE_ID,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Delegate Grok mode changes when leaving upstream modes

When Grok advertises a real ACP mode such as plan, transformGrokSessionResponse appends the synthetic ask option to that same mode list. Selecting Ask then reaches this unconditional handled return instead of the generic ACP setSessionMode path, so a session currently in plan is reported as ask in Paseo while Grok remains in the upstream mode (and if always-approve was enabled but a current-mode update reset currentModeId to plan, this also skips sending /always-approve off).

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds two Grok-specific ACP behaviors that were missing: suppression of model-only user_message_chunk events marked _meta.hideFromScrollback === true (preventing raw <system-reminder> text from appearing in the scrollback), and structured rendering of background task lifecycle events (_x.ai/session/update) as synthetic tool_call timeline items with stable SHA-1–derived callIds so backgrounded → completed transitions project onto the same item.

  • grok-background-tasks.ts owns all Grok-specific parsing (Zod schemas, lifecycle resolution, label/detail/metadata builders); ACPAgentSession only sees two narrow opt-in hooks (shouldSuppressUserMessageChunk, extensionNotificationHandler).
  • deliverStreamEvents is extracted to share history-replay vs live-stream routing between sessionUpdate and extNotification, so extension-driven timeline items land in persistedHistory during load.
  • The always-approve client-side fallback (via autoApproveModeIds) auto-selects the allow option without surfacing a permission UI, covering races where Grok still emits session/request_permission despite the native --always-approve flag.

Confidence Score: 5/5

Safe to merge; changes are well-scoped to Grok-specific paths and do not affect existing ACP providers.

Feature logic is fully contained in grok-background-tasks.ts and grok-acp-agent.ts. The shared ACPAgentSession changes add opt-in hooks that default to no-op for all existing providers. The chunk-suppression filter fires before fallbackAssistantMessageId is cleared, which is the correct ordering to prevent contamination. The hash-based callId eliminates the collision risk noted in prior review. 34 tests pass covering all lifecycle branches. One style-only observation about conditional spreads in attachFallbackModeMetadata has no functional impact.

No files require special attention.

Important Files Changed

Filename Overview
packages/server/src/server/agent/providers/grok-background-tasks.ts New module mapping Grok _x.ai/session/update notifications to synthetic tool_call timeline items; uses SHA-1 hash for stable callId (addressing prior collision concern), validates via Zod, separates lifecycle helpers cleanly.
packages/server/src/server/agent/providers/acp-agent.ts Adds narrow opt-in hooks (shouldSuppressUserMessageChunk, extensionNotificationHandler, autoApproveModeIds, resolveSessionCommand) and extracts deliverStreamEvents for shared history-replay/live-stream delivery; Grok chunk suppression correctly precedes fallbackAssistantMessageId reset.
packages/server/src/server/agent/providers/grok-acp-agent.ts Grok-specific adapter: session command resolution for always-approve, session response transformer injecting modes, and in-session mode writer via slash commands; all logic stays in this file and grok-background-tasks.ts.
packages/server/src/server/agent/providers/grok-acp-agent.test.ts New 883-line test file covering launch flag injection, mode transformer, permission auto-approve, chunk suppression (live and history replay), and the full task lifecycle; directly exercises ACPAgentSession integration points.
packages/server/src/server/agent/providers/grok-background-tasks.test.ts Unit tests for the background task module covering null method, wrong-session filter, malformed payload tolerance, all lifecycle outcomes, and callId non-collision.
packages/server/src/server/agent/providers/generic-acp-agent.ts Exposes the new ACPAgentClient option fields through GenericACPAgentClient so GrokACPAgentClient can extend it; straightforward pass-through forwarding.
packages/server/src/server/agent/provider-registry.ts Registers Grok with canonical modes and provider-specific client creation; follows the same conditional pattern already used for cursor and kiro.
packages/server/src/server/agent/providers/acp-agent.test.ts Two new deriveModesFromACP tests verify that provider fallback metadata (icon, colorTier, isUnattended) is re-attached when mode information arrives via availableModes or config options.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant Grok as Grok Process
    participant Session as ACPAgentSession
    participant BgTasks as grok-background-tasks
    participant Timeline as Timeline / UI

    Note over Session: user_message_chunk (hideFromScrollback=true)
    Grok->>Session: sessionUpdate(user_message_chunk)
    Session->>Session: shouldSuppressUserMessageChunk → true
    Session-->>Timeline: (suppressed)

    Note over Session: user_message_chunk (visible)
    Grok->>Session: sessionUpdate(user_message_chunk)
    Session->>Session: shouldSuppressUserMessageChunk → false
    Session->>Timeline: pushEvent(user_message)

    Note over Session: Background task lifecycle
    Grok->>Session: extNotification(_x.ai/session/update, task_backgrounded)
    Session->>BgTasks: extensionNotificationHandler()
    BgTasks->>BgTasks: buildGrokBackgroundTaskCallId SHA-1
    BgTasks-->>Session: tool_call running
    Session->>Session: deliverStreamEvents
    Session->>Timeline: tool_call running

    Grok->>Session: extNotification(_x.ai/session/update, task_completed)
    Session->>BgTasks: extensionNotificationHandler()
    BgTasks->>BgTasks: resolveGrokTaskCompletionLifecycle
    BgTasks-->>Session: tool_call completed/failed/canceled
    Session->>Timeline: tool_call update

    Note over Session: Always Approve fallback
    Grok->>Session: requestPermission
    Session->>Session: autoApproveModeIds.has(currentMode)
    Session-->>Grok: outcome selected
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant Grok as Grok Process
    participant Session as ACPAgentSession
    participant BgTasks as grok-background-tasks
    participant Timeline as Timeline / UI

    Note over Session: user_message_chunk (hideFromScrollback=true)
    Grok->>Session: sessionUpdate(user_message_chunk)
    Session->>Session: shouldSuppressUserMessageChunk → true
    Session-->>Timeline: (suppressed)

    Note over Session: user_message_chunk (visible)
    Grok->>Session: sessionUpdate(user_message_chunk)
    Session->>Session: shouldSuppressUserMessageChunk → false
    Session->>Timeline: pushEvent(user_message)

    Note over Session: Background task lifecycle
    Grok->>Session: extNotification(_x.ai/session/update, task_backgrounded)
    Session->>BgTasks: extensionNotificationHandler()
    BgTasks->>BgTasks: buildGrokBackgroundTaskCallId SHA-1
    BgTasks-->>Session: tool_call running
    Session->>Session: deliverStreamEvents
    Session->>Timeline: tool_call running

    Grok->>Session: extNotification(_x.ai/session/update, task_completed)
    Session->>BgTasks: extensionNotificationHandler()
    BgTasks->>BgTasks: resolveGrokTaskCompletionLifecycle
    BgTasks-->>Session: tool_call completed/failed/canceled
    Session->>Timeline: tool_call update

    Note over Session: Always Approve fallback
    Grok->>Session: requestPermission
    Session->>Session: autoApproveModeIds.has(currentMode)
    Session-->>Grok: outcome selected
Loading

Reviews (7): Last reviewed commit: "Merge branch 'main' into fix/2182-grok-h..." | Re-trigger Greptile

Comment thread packages/server/src/server/agent/providers/grok-background-tasks.ts
Comment thread packages/server/src/server/agent/providers/grok-background-tasks.ts Outdated
Comment thread packages/server/src/server/agent/providers/grok-background-tasks.ts
jasonhnd added 5 commits July 19, 2026 00:18
Hash raw task_ids for synthetic tool callIds so normalized-character
collisions cannot merge concurrent tasks. Reuse the shared ACP extension
context type, build task metadata without conditional object spreads, and
let generic setSessionMode handle Ask when leaving an upstream ACP mode.
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.

bug(grok/acp): hidden background-task reminders render as raw user messages

1 participant