Skip to content

fix(grok): clipboard, native chat, hooks, sessions, ConPTY KKP#7944

Merged
Jinwoo-H merged 9 commits into
stablyai:mainfrom
bbingz:fix/grok-clipboard-compat
Jul 10, 2026
Merged

fix(grok): clipboard, native chat, hooks, sessions, ConPTY KKP#7944
Jinwoo-H merged 9 commits into
stablyai:mainfrom
bbingz:fix/grok-clipboard-compat

Conversation

@bbingz

@bbingz bbingz commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Grok Build compatibility fixes that remain unique after reconciling with current main and #7869:

  • Restore Grok image attachments and native-chat transcript support.
  • Launch startup prompts through positional argv instead of raw post-start keystrokes.
  • Install valid .* Grok tool hook matchers, add StopFailure, tool previews, and waiting state for ask_user_question.
  • Resolve chat_history.jsonl under GROK_HOME and long-cwd slug layouts.
  • Keep Kitty keyboard protocol available for Orca-launched Grok on Windows ConPTY while preserving the default ConPTY safety behavior for other agents.
  • Document Grok as an OSC 52 clipboard-write consumer while keeping the security-sensitive setting default-off and OSC 52 queries disabled.

Reconciliation with main

Screenshots

No visual change.

Testing

  • pnpm lint
  • pnpm typecheck
  • Focused Vitest: 10 files / 133 tests covering Grok paths, auth, hooks, transcript decoding, native chat, AI Vault, and terminal keyboard protocol
  • pnpm build
  • Added or retained regression coverage for each compatibility path
  • Focused oxlint, pnpm check:max-lines-ratchet, and git diff --check

AI Review Report

The branch was rebased onto current main and reviewed file-by-file against #7869 and #7841. The review separated semantic overlap from files that merely share Grok types or locale catalogs.

It found that usage code had already been removed, but the remote XTVERSION/TERM commit exactly duplicated #7841 and was dropped. It also found three separate GROK_HOME implementations across usage, AI Vault, and this PR; these now use one shared resolver. The latest TypeScript configuration exposed an overly strict environment-map type during reconciliation, which was corrected and revalidated.

Cross-platform review covered macOS/Linux local behavior, Windows ConPTY KKP policy, SSH-safe path handling, and host-vs-guest environment boundaries. The #7841 remote PTY behavior is no longer duplicated here.

Security Audit

  • No new network, auth, dependency, or IPC surface.
  • Main's Grok usage integration remains read-only and authoritative.
  • OSC 52 writes remain default-off and user-controlled; OSC 52 clipboard queries remain disabled.
  • Session IDs are validated and filesystem searches are bounded.
  • Remote hook installation does not inject the local host's GROK_HOME into a guest.

Notes

This PR intentionally no longer contains remote TERM/XTVERSION behavior; see #7841. Managed Grok accounts and owned-home isolation remain out of scope per maintainer direction.

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds Grok support across native chat session resolution, transcript decoding, hook normalization, renderer availability, pane resolution, image paste, and startup handling. It updates Grok hook matchers and installation paths, forces terminal identity variables for PTY environments, filters orphaned terminal query replies, preserves Kitty keyboard support for Grok on Windows ConPTY, and updates OSC 52 clipboard defaults, documentation, settings text, tests, and translations.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.36% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and clearly summarizes the main Grok-related changes across clipboard, native chat, hooks, sessions, and ConPTY.
Description check ✅ Passed The description follows the required template and covers summary, screenshots, testing, AI review, security audit, and notes with relevant detail.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/main/native-chat/session-file-resolver.ts (1)

129-147: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider targeted directory listing instead of full tree walk.

resolveGrokSessionFile uses walkSessionFiles to traverse the entire sessions tree and filter by basename(dirname(path)) === sessionId. Given the known 2-level structure (sessionsDir/encodeURIComponent(cwd)/sessionId/chat_history.jsonl), listing the top-level directories and checking for join(dir, sessionId, 'chat_history.jsonl') directly would avoid reading every .jsonl file across all sessions — a meaningful improvement for users with many working directories.

⚡ Proposed targeted lookup
 async function resolveGrokSessionFile(
   sessionId: string,
   sessionsDir: string
 ): Promise<string | null> {
-  // Why: Grok nests chat_history.jsonl under encodeURIComponent(cwd)/sessionId/
-  // (same layout agent-hook-listener getGrokChatHistoryPath uses). Walk the
-  // sessions tree for that exact leaf name under a directory named sessionId.
+  // Why: Grok nests chat_history.jsonl under encodeURIComponent(cwd)/sessionId/
+  // (same layout agent-hook-listener getGrokChatHistoryPath uses). List the
+  // top-level cwd directories and probe for the exact leaf path.
   if (!existsSync(sessionsDir)) {
     return null
   }
-  const targetName = 'chat_history.jsonl'
-  const files = await walkSessionFiles(sessionsDir, 'grok', [], {
-    extensions: new Set(['.jsonl']),
-    filePredicate: (path) =>
-      basename(path) === targetName && basename(dirname(path)) === sessionId
-  })
-  return files[0] ?? null
+  const targetName = 'chat_history.jsonl'
+  const entries = await readdir(sessionsDir, { withFileTypes: true })
+  for (const entry of entries) {
+    if (!entry.isDirectory()) continue
+    const candidate = join(sessionsDir, entry.name, sessionId, targetName)
+    if (existsSync(candidate)) {
+      return candidate
+    }
+  }
+  return null
 }
src/main/native-chat/transcript-line-decoders.grok.test.ts (1)

1-54: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add tests for backend_tool_call, tool_call, and tool_result record types.

The suite covers user, assistant (tool-call-only), reasoning, and system branches, but the backend_tool_call/tool_call path (lines 119–132 in the decoder) and tool_result path (lines 134–149) — which include multi-fallback name extraction and isError normalization — are untested.

🧪 Suggested additional test cases
  it('decodes backend_tool_call records', () => {
    const line = JSON.stringify({
      type: 'backend_tool_call',
      kind: { tool_type: 'grep', pattern: 'foo' },
      id: 'btc-1'
    })
    expect(decodeGrokTranscriptLine(line, 'fb-5')).toEqual({
      id: 'btc-1',
      role: 'assistant',
      blocks: [{ type: 'tool-call', name: 'grep', input: { tool_type: 'grep', pattern: 'foo' } }],
      timestamp: null,
      source: 'transcript'
    })
  })

  it('decodes tool_result records with error flag', () => {
    const line = JSON.stringify({
      type: 'tool_result',
      content: 'Command failed',
      is_error: true,
      id: 'tr-1'
    })
    expect(decodeGrokTranscriptLine(line, 'fb-6')).toEqual({
      id: 'tr-1',
      role: 'tool',
      blocks: [{ type: 'tool-result', output: 'Command failed', isError: true }],
      timestamp: null,
      source: 'transcript'
    })
  })

  it('decodes assistant with both text content and tool_calls', () => {
    const line = JSON.stringify({
      type: 'assistant',
      content: [{ type: 'text', text: 'Running grep now' }],
      tool_calls: [{ id: 'c2', name: 'grep', arguments: '{"pattern":"bar"}' }],
      id: 'asst-2'
    })
    expect(decodeGrokTranscriptLine(line, 'fb-7')).toEqual({
      id: 'asst-2',
      role: 'assistant',
      blocks: [
        { type: 'text', text: 'Running grep now' },
        { type: 'tool-call', name: 'grep', input: { pattern: 'bar' } }
      ],
      timestamp: null,
      source: 'transcript'
    })
  })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 7dabc9bb-2fe6-446a-a69d-433e7ad1310e

📥 Commits

Reviewing files that changed from the base of the PR and between 8adfef4 and 1e901ca.

📒 Files selected for processing (20)
  • src/main/codex-accounts/runtime-home-service.test.ts
  • src/main/codex-accounts/service.test.ts
  • src/main/native-chat/session-file-resolver.test.ts
  • src/main/native-chat/session-file-resolver.ts
  • src/main/native-chat/transcript-line-decoders.grok.test.ts
  • src/main/native-chat/transcript-line-decoders.ts
  • src/main/native-chat/transcript-reader.ts
  • src/main/native-chat/transcript-watch.ts
  • src/renderer/src/components/native-chat/NativeChatView.test.tsx
  • src/renderer/src/components/native-chat/native-chat-availability.test.ts
  • src/renderer/src/components/native-chat/native-chat-availability.ts
  • src/renderer/src/components/native-chat/native-chat-image-paste.test.ts
  • src/renderer/src/components/native-chat/native-chat-image-paste.ts
  • src/renderer/src/components/native-chat/native-chat-pane-resolution.test.ts
  • src/renderer/src/components/native-chat/use-native-chat-toggle-shortcut.ts
  • src/renderer/src/components/settings/TerminalInteractionSection.tsx
  • src/renderer/src/lib/native-chat-supported-agent.ts
  • src/renderer/src/lib/tui-agent-startup.test.ts
  • src/shared/constants.ts
  • src/shared/tui-agent-config.ts

Comment thread src/renderer/src/components/settings/TerminalInteractionSection.tsx
Comment thread src/shared/constants.ts Outdated
@bbingz bbingz changed the title fix(grok): restore clipboard and native-chat parity fix(grok): clipboard, native chat, hooks, remote TERM, ConPTY KKP Jul 9, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/shared/grok-session-paths.ts (1)

53-71: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add sessionId validation to prevent path traversal in buildGrokChatHistoryPathCandidates.

sessionId is used directly in join(args.sessionsDir, encoded, sessionId, GROK_CHAT_HISTORY_FILE) without checking for path traversal characters. While current callers (e.g., getGrokChatHistoryPath in agent-hook-listener.ts) validate via isSafeGrokSessionId, this is a public exported function — a future caller that skips validation could traverse outside sessionsDir with a crafted sessionId like ../../etc.

🛡️ Proposed defense-in-depth validation
 export function buildGrokChatHistoryPathCandidates(args: {
   sessionId: string
   cwd?: string | null
   sessionsDir: string
 }): string[] {
   const sessionId = args.sessionId.trim()
-  if (!sessionId) {
+  if (!sessionId || /[/\\]/.test(sessionId) || sessionId.includes('..')) {
     return []
   }

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: e7a3a876-5c96-4b74-9be3-8e6e4454ee4d

📥 Commits

Reviewing files that changed from the base of the PR and between e564d8b and 6d2ef9a.

📒 Files selected for processing (20)
  • src/main/agent-hooks/remote-hook-service-installers.test.ts
  • src/main/grok/hook-service.test.ts
  • src/main/grok/hook-service.ts
  • src/main/native-chat/session-file-resolver.test.ts
  • src/main/native-chat/session-file-resolver.ts
  • src/main/providers/ssh-pty-provider.test.ts
  • src/main/providers/ssh-pty-provider.ts
  • src/relay/pty-handler.ts
  • src/renderer/src/components/terminal-pane/pty-connection.test.ts
  • src/renderer/src/components/terminal-pane/pty-connection.ts
  • src/renderer/src/components/terminal-pane/use-terminal-pane-lifecycle.ts
  • src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts
  • src/renderer/src/lib/pane-manager/terminal-keyboard-protocol.test.ts
  • src/renderer/src/lib/pane-manager/terminal-keyboard-protocol.ts
  • src/shared/agent-hook-listener.test.ts
  • src/shared/agent-hook-listener.ts
  • src/shared/grok-session-paths.test.ts
  • src/shared/grok-session-paths.ts
  • src/shared/terminal-query-reply.test.ts
  • src/shared/terminal-query-reply.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/main/native-chat/session-file-resolver.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/main/native-chat/transcript-line-decoders-claude.ts (1)

52-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract parseTimestamp to a shared module.

This identical parseTimestamp wrapper is duplicated in transcript-line-decoders-codex.ts (lines 136-139) and transcript-line-decoders-grok.ts (lines 179-182). Since transcript-record-blocks.ts already serves as the shared utility module for these decoders (exporting claudeContentBlocks and toolResultOutput), moving parseTimestamp there would eliminate the triplication and provide a single point of change for timestamp null-handling logic.

♻️ Proposed refactor

In src/main/native-chat/transcript-record-blocks.ts, add:

+export function parseTimestamp(value: unknown): number | null {
+  const parsed = timestampMs(value)
+  return Number.isFinite(parsed) ? parsed : null
+}

Then in each decoder file (transcript-line-decoders-claude.ts, transcript-line-decoders-codex.ts, transcript-line-decoders-grok.ts), replace the local parseTimestamp with an import:

-import { timestampMs } from '../ai-vault/session-scanner-values'
+import { timestampMs } from '../ai-vault/session-scanner-values'
+import { parseTimestamp } from './transcript-record-blocks'

And remove the local parseTimestamp definition from each file.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f83e5077-8100-4234-a194-2c94e53e9457

📥 Commits

Reviewing files that changed from the base of the PR and between 6d2ef9a and a27892e.

📒 Files selected for processing (8)
  • src/main/grok/hook-service.test.ts
  • src/main/grok/hook-service.ts
  • src/main/native-chat/transcript-line-decoders-claude.ts
  • src/main/native-chat/transcript-line-decoders-codex.ts
  • src/main/native-chat/transcript-line-decoders-grok.ts
  • src/main/native-chat/transcript-line-decoders.ts
  • src/shared/constants.ts
  • src/shared/types.ts
✅ Files skipped from review due to trivial changes (1)
  • src/shared/types.ts

@bbingz bbingz force-pushed the fix/grok-clipboard-compat branch 2 times, most recently from 604ef40 to 0eb05c7 Compare July 10, 2026 01:50
bbingz and others added 7 commits July 10, 2026 17:49
Grok CLI already supports argv prompts, OSC 52 copy, and image paste chips.
Orca was blocking those paths: stdin-after-start keystroke injection, OSC 52
writes default-off, image-attachment denylist, and native-chat allowlist.

- Launch Grok with positional argv prompts
- Default OSC 52 TUI clipboard writes on (still user-toggleable)
- Treat Grok as image-attachment capable
- Parse ~/.grok/.../chat_history.jsonl for native chat

OSC 52 clipboard *query* remains ignored by design (host clipboard exfil risk);
xAI docs only require OSC 52 write for remote copy.
Update terminalAllowOsc52Clipboard type docs for the true default, and
refresh locale strings so settings UI mentions Grok alongside other TUIs.
Grok tool-event matchers are real regexes; bare `*` failed as match-all.
Install `.*` for Pre/Post tool hooks, add StopFailure for API-error ends,
recognize Grok-native tool input keys, and map ask_user_question PreToolUse
to waiting with interactivePrompt (Kimi-style live card path).
Centralize Grok session path helpers so hooks and native-chat honor
GROK_HOME and find chat_history.jsonl by session id when the cwd group
is slug-encoded (encoded name > 255 bytes) instead of only
encodeURIComponent(cwd).
Local Windows ConPTY withholds KKP so CSI-u-blind CLIs (e.g. Antigravity)
keep Enter/nav working (stablyai#2434). Grok needs KKP for Ctrl+Enter interject and
modified-Enter newline chords; blanking the advertisement for Orca-launched
Grok left those actions broken.

- Prefer KKP when tuiAgent is grok despite ConPTY withhold
- Wire launchAgent from tab/startup into keyboard protocol options
… hooks

- Keep terminalAllowOsc52Clipboard default false (clipboard exfil risk)
- Split transcript-line-decoders under max-lines without suppressions
- Install local Grok hooks under resolveGrokHomeDir() / GROK_HOME
@bbingz bbingz force-pushed the fix/grok-clipboard-compat branch from 0eb05c7 to b6abb2f Compare July 10, 2026 10:00
@bbingz bbingz changed the title fix(grok): clipboard, native chat, hooks, remote TERM, ConPTY KKP fix(grok): clipboard, native chat, hooks, sessions, ConPTY KKP Jul 10, 2026

@Jinwoo-H Jinwoo-H left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks — I reviewed and extended this end to end.

  • Verified real Grok 0.2.93 launch, hook attribution, Native Chat transcript loading, image paste/read, and exact responses.
  • Hardened transcript discovery/filtering and ordering, hook lifecycle, custom GROK_HOME handling, and prompt argument separation.
  • Verified OSC 52 remains default-off and works when explicitly enabled.
  • Exercised a real Linux SSH relay, including custom/default TERM, environment deletion, framed XTVERSION replies, custom remote Grok hooks, and hook round-trip.
  • Fixed the final node-pty boundary that was overwriting custom remote TERM values.
  • Added regression coverage for the races and compatibility cases found during review.

@Jinwoo-H Jinwoo-H merged commit 96d1fa1 into stablyai:main Jul 10, 2026
4 checks passed
brennanb2025 added a commit that referenced this pull request Jul 10, 2026
#8174)

launch-agent-in-new-tab.ts crossed the 300-line oxlint max-lines limit
(303 counted lines) after #5510 and #7944 both grew it; verify only runs
on PRs, so the over-limit state landed on main via a merge race and now
fails lint for every open PR.

Move the web-runtime host launch branch (stale-local-tab pruning plus
createWebRuntimeSessionTerminal call) into launch-agent-web-host-tab.ts.
No behavior change; the i18n key is kept so locale catalogs are
untouched.

Co-authored-by: Brennan Benson <brennanbenson@Brennans-MacBook-Pro.local>
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.

2 participants