Skip to content

feat(cli): let gist create target a provider session - #1441

Open
pedramamini wants to merge 1 commit into
mainfrom
fix/1440-gist-create-session-target
Open

feat(cli): let gist create target a provider session#1441
pedramamini wants to merge 1 commit into
mainfrom
fix/1440-gist-create-session-target

Conversation

@pedramamini

@pedramamini pedramamini commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

Closes #1440

The gap

maestro-cli gist create <agent-id> took only an agent id, so it published the transcripts of whatever AI tabs that agent had open in the desktop app. Headless callers address a conversation by its provider session id (send -s <id>) and have no tab at all, so Maestro Relay's /gist in a channel discussing topic B published an unrelated desktop conversation about topic A. Gists are readable by anyone holding the URL, so that is a leak rather than a surprise. Playbooks, Cue pipelines, and CI have the same gap: send is session-addressable, gist create was not.

The change

maestro-cli gist create <agent-id> --session <session-id>

--session is threaded through the WebSocket bridge as agentSessionId (CLI -> create_gist message -> CreateGistCallback -> renderer). The renderer resolves the transcript in two steps:

  1. the open AI tab holding that agentSessionId, when one exists (logs are already in memory, and every provider has them);
  2. otherwise the provider's stored transcript, read through the existing agentSessions.read path, so SSH remotes work via resolveSessionProjectPath.

Omitting the flag keeps today's behavior byte for byte.

Deliberate non-fallbacks

  • A --session that cannot be resolved fails. Falling back to the agent's open tabs is precisely the substitution this option exists to prevent, and the caller cannot tell the difference from the returned URL.
  • A blank --session is rejected at the CLI (INVALID_SESSION) and again at the bridge, rather than being treated as absent.
  • The read is capped at 10k messages and the body says so when it truncates - a silently cut transcript reads as a complete one to whoever opens the gist.

The success payload echoes agentSessionId so a caller can confirm what was published.

Tests

  • src/__tests__/cli/commands/gist.test.ts (new): flag is forwarded, absent when unused, blank rejected, desktop failure surfaced.
  • useRemoteIntegration.test.ts: publishes only the matching tab, reads the provider transcript when no tab holds the session, fails rather than falling back to the tabs, and the unflagged path still publishes every tab.
  • messageHandlers.test.ts: agentSessionId forwarded; blank and non-string rejected.

npm run lint, npx eslint src/, prettier, and the cli / web-server / preload suites (1910 tests) are green locally. Docs updated in docs/cli.md plus the generated docs/cli-reference.md.

Summary by CodeRabbit

  • New Features

    • Added -s, --session <id> to gist create for publishing a specific provider session transcript.
    • Remote gist creation now supports targeted session transcripts, including headless sessions.
    • Responses identify the published session when applicable.
  • Bug Fixes

    • Invalid or missing session transcripts now return clear errors without falling back to unrelated open tabs.
  • Documentation

    • Updated CLI usage, transcript behavior, failure scenarios, and error codes.

`gist create` took only an agent id, so it published whatever AI tabs that
agent had open in the desktop app. Headless callers address a conversation by
its provider session id (`send -s <id>`) and have no tab, so `/gist` from a
chat bridge published an unrelated desktop conversation - and a gist is
readable by anyone holding the URL.

Adds `--session <id>`, threaded through the WebSocket bridge as
`agentSessionId`. The renderer publishes the open tab holding that session
when there is one, else the provider's stored transcript (SSH remotes
included, via the existing `agentSessions.read` path).

There is no fallback in either direction: a `--session` that cannot be
resolved fails, because publishing the open tabs instead is the substitution
this option exists to prevent. A blank `--session` is rejected at both the CLI
and the bridge for the same reason. Omitting the flag keeps today's behavior.

Truncates at 10k messages and says so in the body rather than silently
publishing a partial transcript.
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: d56b20fe-92e8-4dc7-8535-7b7c1b0ece14

📥 Commits

Reviewing files that changed from the base of the PR and between 9656c12 and bd547a9.

📒 Files selected for processing (15)
  • docs/cli-reference.md
  • docs/cli.md
  • src/__tests__/cli/commands/gist.test.ts
  • src/__tests__/main/web-server/handlers/messageHandlers.test.ts
  • src/__tests__/renderer/hooks/useRemoteIntegration.test.ts
  • src/cli/commands/gist.ts
  • src/cli/index.ts
  • src/main/preload/process.ts
  • src/main/web-server/WebServer.ts
  • src/main/web-server/handlers/messageHandlers.ts
  • src/main/web-server/managers/CallbackRegistry.ts
  • src/main/web-server/types.ts
  • src/main/web-server/web-server-factory.ts
  • src/renderer/global.d.ts
  • src/renderer/hooks/remote/useRemoteIntegration.ts

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Gist creation now accepts an optional provider session ID. The ID travels through the CLI, web-server, callback, and IPC layers. The renderer publishes a matching live-tab transcript or reads a headless session transcript. Existing open-tab behavior remains unchanged without the option.

Changes

Gist session targeting

Layer / File(s) Summary
CLI session option
src/cli/index.ts, src/cli/commands/gist.ts, src/__tests__/cli/commands/gist.test.ts, docs/cli-reference.md, docs/cli.md
gist create accepts --session, rejects blank values with INVALID_SESSION, forwards valid IDs, and reports agentSessionId.
Session ID transport and validation
src/main/web-server/types.ts, src/main/web-server/handlers/messageHandlers.ts, src/main/web-server/managers/CallbackRegistry.ts, src/main/web-server/WebServer.ts, src/main/web-server/web-server-factory.ts, src/main/preload/process.ts, src/renderer/global.d.ts, src/__tests__/main/web-server/handlers/messageHandlers.test.ts
The optional session ID moves through the WebSocket, callback registry, IPC, and renderer contracts. Provided values must be non-empty strings.
Session transcript selection and gist generation
src/renderer/hooks/remote/useRemoteIntegration.ts, src/__tests__/renderer/hooks/useRemoteIntegration.test.ts
The renderer uses a matching live tab or loads the provider transcript from storage. Targeted sessions produce session-specific gist content and filenames. Requests without a target retain all-tab behavior.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to bd547

The change adds optional session-specific gist creation while preserving existing behavior when unused; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: reachrazamair

Sequence Diagram(s)

sequenceDiagram
  participant MaestroCLI
  participant WebServer
  participant RendererIntegration
  participant AgentSessionStore
  participant GitIntegration
  MaestroCLI->>WebServer: create_gist with agentSessionId
  WebServer->>RendererIntegration: forward session ID
  RendererIntegration->>AgentSessionStore: load targeted transcript when no matching live tab exists
  AgentSessionStore-->>RendererIntegration: transcript or error
  RendererIntegration->>GitIntegration: create session-specific gist
  GitIntegration-->>RendererIntegration: gist URL
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: allowing gist create to target a provider session.
Linked Issues check ✅ Passed The changes satisfy issue #1440. They add --session, pass agentSessionId through the bridge, support live and headless provider sessions, reject invalid sessions, preserve existing behavior withou…
Out of Scope Changes check ✅ Passed The changes remain within scope. The implementation, tests, API plumbing, transcript handling, validation, and documentation directly support provider-session gist creation.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 13 files. (2 skipped: 2 …
Full details: Linked Issues check

Explanation

The changes satisfy issue #1440. They add --session, pass agentSessionId through the bridge, support live and headless provider sessions, reject invalid sessions, preserve existing behavior without --session, and update tests and documentation.

Full details: Docstring Coverage

Explanation

Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 13 files. (2 skipped: 2 unsupported.)

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1440-gist-create-session-target

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.

@greptile-apps

greptile-apps Bot commented Aug 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds a session-addressable gist create --session flow, carrying a provider session ID from the CLI through the WebSocket and preload bridges to renderer-side transcript retrieval.

  • Preserves the existing all-open-tabs behavior when the option is omitted.
  • Uses matching in-memory tab logs first, then reads provider-backed local or SSH history.
  • Adds validation, truncation disclosure, documentation, and tests across the command and bridge layers.
  • The storage fallback currently permits cross-project Codex and OpenCode transcript selection by session ID.

Confidence Score: 3/5

This PR should not merge until targeted stored-session reads are constrained to the requested agent project, because the current Codex and OpenCode path can publish another project's transcript.

The new externally selectable session ID reaches provider storage and then gist creation, while two supported storage adapters resolve that ID globally instead of validating it against the requested project.

Files Needing Attention: src/renderer/hooks/remote/useRemoteIntegration.ts and the Codex/OpenCode session-storage ownership boundary

Security Review

The targeted storage fallback does not enforce that Codex or OpenCode session IDs belong to the requested agent project. An authenticated caller who knows another session ID can therefore publish that transcript as a URL-readable gist.

Important Files Changed

Filename Overview
src/renderer/hooks/remote/useRemoteIntegration.ts Adds targeted live/stored transcript selection, but the stored fallback relies on provider adapters that do not uniformly enforce project ownership.
src/main/web-server/handlers/messageHandlers.ts Validates and forwards the optional provider session ID while preserving the prior request defaults.
src/cli/commands/gist.ts Adds blank-session validation, forwards the normalized ID, and echoes it in successful machine-readable output.
src/main/preload/process.ts Keeps the new callback argument aligned across the preload event bridge.
src/tests/renderer/hooks/useRemoteIntegration.test.ts Covers matching-tab selection, stored fallback, failure without tab fallback, and legacy behavior, but not cross-project provider-session ownership.

Sequence Diagram

sequenceDiagram
  participant CLI
  participant WS as WebSocket handler
  participant Renderer
  participant Storage as Provider session storage
  participant GitHub
  CLI->>WS: create_gist(agentId, agentSessionId)
  WS->>Renderer: remote:createGist
  alt Matching open tab
    Renderer->>Renderer: Format matching tab logs
  else No matching tab
    Renderer->>Storage: read(toolType, projectPath, agentSessionId)
    Storage-->>Renderer: Normalized transcript
  end
  Renderer->>GitHub: Create gist from transcript
  GitHub-->>CLI: Gist URL
Loading

Reviews (1): Last reviewed commit: "feat(cli): let `gist create` target a pr..." | Re-trigger Greptile

Comment on lines +82 to +88
try {
result = await window.maestro.agentSessions.read(
session.toolType,
projectPathForSessions,
agentSessionId,
{ offset: 0, limit: GIST_SESSION_MESSAGE_LIMIT },
sshRemoteId

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 security Cross-project transcript export

When a caller supplies a Codex or OpenCode session ID from another project, this fallback passes it to provider storage that resolves sessions globally rather than enforcing projectPath, causing the unrelated transcript to be published to a URL-readable gist. Validate that the selected provider session belongs to the requested agent and project before creating the gist.

How this was verified: The new caller-controlled ID was traced through agentSessions.read to the Codex and OpenCode readers, which locate transcripts by session ID without constraining them to the supplied project.

Knowledge Base Used:

@pedramamini

Copy link
Copy Markdown
Collaborator Author

CI note: the test leg is red with 4 failures, all in FilePreview.test.tsx > bare font-zoom keys. Those are pre-existing on main - run 32991217539 on the base commit 9656c12 fails with the identical 4. Nothing in this PR's area failed: 34754 passed, and the gist / useRemoteIntegration / messageHandlers suites are all green. lint-and-format and CodeQL pass.

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.

maestro-cli: gist create cannot target a session (only an agent's open desktop tab)

1 participant