Skip to content

feat(gmail): reference attachments by index (--use-indexed-attachment-ids) - #963

Merged
steipete merged 5 commits into
openclaw:mainfrom
ronny-rentner:feat/gmail-attachment-by-index
Aug 9, 2026
Merged

feat(gmail): reference attachments by index (--use-indexed-attachment-ids)#963
steipete merged 5 commits into
openclaw:mainfrom
ronny-rentner:feat/gmail-attachment-by-index

Conversation

@ronny-rentner

@ronny-rentner ronny-rentner commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Stacked on #962. This builds on feat/gmail-include-attachments; that PR's commit is shown here too and will drop out once #962 merges. Review/merge #962 first.

Summary

Lets Gmail attachments be referenced by a stable 0-based index instead of the long, opaque attachmentId, behind a single opt-in flag.

--use-indexed-attachment-ids (env GOG_GMAIL_USE_INDEXED_ATTACHMENT_IDS) is added to the six commands that render or accept an attachment id: messages search, get, thread get, thread attachments, drafts get, and attachment. When it's on:

  • Output — those commands emit attachmentIndex (a number) in place of attachmentId (a ~200-char opaque string), in both JSON and text. The long id is not emitted at all.
  • Download inputgmail attachment <messageId> <index> takes the 0-based index; the index resolves to the real attachment (the real id still drives the actual download API call). A non-numeric argument is rejected in this mode.
  • Saved filename — the default download filename uses the index (<messageId>_<index>_...).

Default behaviour is unchanged: without the flag the argument and the output are the real attachmentId, and a numeric argument is treated as a real id, not an index (the index and the id are never silently mixed).

Motivation

The attachmentId is a ~200-char opaque base64url string, so emitting one per attachment in a listing — and passing it back to download — is token-expensive for an agent. A 0-based position is 1–2 characters and is a stable reference: a message's MIME structure is fixed, so the Nth attachment is the same across calls, and gmail attachment re-resolves the index to the current attachmentId at download time. With the flag on, an agent lists attachments and downloads them purely by index and never handles a long id.

User-facing changes

  • New flag --use-indexed-attachment-ids (env GOG_GMAIL_USE_INDEXED_ATTACHMENT_IDS) on the six attachment-handling gmail commands.
  • In that mode: output carries attachmentIndex (number) instead of attachmentId; gmail attachment takes the index; the saved filename uses the index.

Implementation notes

  • attachmentInfo carries both AttachmentID and a 0-based AttachmentIndex, stamped once when attachments are collected (collectAttachments).
  • Output types (attachmentOutput and the download-result summaries) have distinct attachmentId (string, omitempty) and attachmentIndex (number, omitempty) fields; a stringified index is never written into an id field.
  • The one index↔id resolution happens once, at the gmail attachment argument. The env var applies the setting globally through each command's env-defaulted flag, mirroring gogcli's existing --include-passwords / GOG_ZOOM_INCLUDE_PASSWORDS.

Testing

  • attachmentByIndex (valid / out-of-range / negative); index resolves and downloads; index out of range errors.
  • Indexed mode: output surfaces attachmentIndex (unit + gmail get end-to-end), a non-numeric argument is rejected, the filename uses the index; a numeric argument without the flag is treated as a raw id (download fails); env-var enables the mode.
  • make fmt / make lint clean; command docs regenerated. Verified against a live account.

@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: 17bed7fbce

ℹ️ 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 thread internal/cmd/gmail_get.go
attachments := collectAttachments(msg.Payload)
if len(attachments) > 0 {
payload["attachments"] = attachmentOutputs(attachments)
payload["attachments"] = attachmentOutputs(attachments, c.UseIndexedAttachmentIDs)

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 Remove opaque IDs from raw JSON in indexed mode

When gmail get --json --use-indexed-attachment-ids runs without --sanitize-content, this converts only the convenience attachments array while payload["message"] still serializes every original message.payload...body.attachmentId. The same issue is more severe in thread get and drafts get, where non-sanitized JSON without downloads has no indexed attachment list at all, so enabling the flag still outputs only opaque IDs. Rewrite or omit attachment IDs in the embedded API objects whenever indexed mode is enabled.

Useful? React with 👍 / 👎.

}

func downloadAttachmentOutputs(ctx context.Context, svc *gmail.Service, messageID string, attachments []attachmentInfo, dir string) ([]attachmentDownloadOutput, error) {
func downloadAttachmentOutputs(ctx context.Context, svc *gmail.Service, messageID string, attachments []attachmentInfo, dir string, useIndexedAttachmentIDs bool) ([]attachmentDownloadOutput, error) {

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 Honor indexed names during bulk downloads

When indexed mode is used with drafts get --download, thread get --download, or thread attachments --download, this function uses the new flag only to format the returned metadata; it still calls downloadAttachment, which constructs the saved filename from the first eight characters of the opaque attachment ID. Consequently the downloaded path does not contain the advertised index and continues exposing the opaque ID, unlike the single-attachment command. Pass an index-aware filename reference into the download path construction.

Useful? React with 👍 / 👎.

@ronny-rentner
ronny-rentner force-pushed the feat/gmail-attachment-by-index branch from 17bed7f to 0350e79 Compare August 7, 2026 09:32
@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal priority bug or improvement with limited blast radius. labels Aug 7, 2026
@clawsweeper

clawsweeper Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed August 8, 2026, 11:33 PM ET / August 9, 2026, 03:33 UTC.

ClawSweeper review

What this changes

This PR adds an opt-in Gmail flag and environment variable that replace attachment IDs with zero-based indexes in attachment listings and downloads.

Merge readiness

Blocked until real behavior proof is added - 5 items remain

Keep open. The current main branch does not implement this opt-in attachment-index contract, and the current patch resolves the prior output-path findings; it still needs maintainer product direction and inspectable real Gmail proof before merge.

Priority: P2
Reviewed head: f9bced26fea3e61a34dc48e1480d93bd31b9676b
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) The patch is focused and resolves the prior review findings, but missing inspectable real behavior proof remains a merge gate.
Proof confidence 🧂 unranked krab (1/6) Needs real behavior proof before merge: The PR claims live-account verification but provides no inspectable redacted terminal output, recording, logs, or artifact from the current head. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The PR claims live-account verification but provides no inspectable redacted terminal output, recording, logs, or artifact from the current head. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 4 items Current main behavior: Current main’s shared attachment model exposes opaque attachment IDs; it has no attachment-index field or indexed-reference mode.
Current PR implementation: The branch adds indexed output fields, index-to-ID resolution before download, raw-payload ID stripping, and index-aware download filenames across the shared Gmail attachment paths.
Merged prerequisite provenance: The related attachment-listing work was merged into current main as the immediate prerequisite; this PR adds a distinct index-reference mode on top of it.
Findings None None.
Security None None.

How this fits together

Gmail commands fetch MIME payloads, collect attachment metadata, render it in text or JSON, and pass a selected attachment reference to Gmail’s download API. This PR changes that shared metadata and reference boundary across message, thread, draft, and download commands.

flowchart LR
  A[Gmail message or thread] --> B[Attachment collection]
  B --> C[Indexed-reference option]
  C --> D[Text and JSON output]
  C --> E[Attachment download lookup]
  E --> F[Gmail attachment API]
  F --> G[Saved file]
Loading

Decision needed

Question Recommendation
Should gogcli add a global opt-in flag and environment variable that replace Gmail attachment IDs with positional indexes across output and download workflows? Sponsor the opt-in contract: Keep the default attachment-ID behavior, accept indexed references only when explicitly enabled, and require real Gmail proof before merge.

Why: The patch adds a new CLI and environment-backed machine contract rather than repairing an established behavior; source inspection cannot choose its long-term API direction.

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The PR claims live-account verification but provides no inspectable redacted terminal output, recording, logs, or artifact from the current head. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Resolve merge risk (P1) - The new environment-backed mode defines a distinct machine-readable attachment-reference contract and needs explicit maintainer approval.
  • Resolve merge risk (P1) - The claimed live-account verification is not inspectable from the PR body or discussion, so real Gmail behavior at the current head remains unconfirmed.
  • Complete next step (P2) - A maintainer must choose the new attachment-reference contract, and the contributor must provide real behavior proof; neither is an autonomous repair task.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production versus test delta production +242/-112, tests +311/-5, docs +6 The feature changes shared attachment rendering and download behavior while adding focused coverage across its command surfaces.
Command surface 6 Gmail commands extended; 19 files affected Review needs to keep the index contract consistent across listings, raw JSON, drafts, threads, and download paths.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Approve the opt-in index contract only if desired, preserve the existing attachment-ID default, and add redacted current-head Gmail evidence showing indexed listing output followed by a successful indexed download.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Approve the opt-in index contract only if desired, preserve the existing attachment-ID default, and add redacted current-head Gmail evidence showing indexed listing output followed by a successful indexed download.

Do we have a high-confidence way to reproduce the issue?

Not applicable as a bug reproduction: this is a new opt-in feature. The branch contains focused mocked command coverage, but no inspectable current-head live Gmail transcript or artifact.

Is this the best way to solve the issue?

Unclear: the implementation consistently keeps the existing default and uses an explicit mode, but maintainers must decide whether this new CLI and environment contract belongs in the product.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1e9a712c91b5.

Labels

Label changes:

  • remove merge-risk: 🚨 compatibility: Current PR review selected no merge-risk labels.

Label justifications:

  • P2: This is a bounded Gmail CLI feature with a new optional workflow rather than an urgent existing-behavior regression.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🐚 platinum hermit.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The PR claims live-account verification but provides no inspectable redacted terminal output, recording, logs, or artifact from the current head. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

  • Current main behavior: Current main’s shared attachment model exposes opaque attachment IDs; it has no attachment-index field or indexed-reference mode. (internal/cmd/gmail_attachments.go:13, 1e9a712c91b5)
  • Current PR implementation: The branch adds indexed output fields, index-to-ID resolution before download, raw-payload ID stripping, and index-aware download filenames across the shared Gmail attachment paths. (internal/cmd/gmail_attachments.go:13, f9bced26fea3)
  • Merged prerequisite provenance: The related attachment-listing work was merged into current main as the immediate prerequisite; this PR adds a distinct index-reference mode on top of it. (internal/cmd/gmail_messages.go:27, 9842e64b7682)
  • Release/main check: The PR head is not contained by a local release tag or current main, so the requested behavior remains unshipped and unimplemented on main. (f9bced26fea3)

Likely related people:

  • Peter Steinberger: Current-main blame attributes the shared attachment model to this history, and the current main tip includes adjacent Gmail attachment documentation work. (role: original attachment subsystem author and recent adjacent contributor; confidence: high; commits: 4747fb05a429, 1e9a712c91b5; files: internal/cmd/gmail_attachments.go, internal/cmd/gmail_attachment.go)
  • Ronny Rentner: The immediately related merged feature established attachment metadata in Gmail search, which this PR extends with a new reference mode. (role: introduced merged message-search attachment support; confidence: high; commits: 9842e64b7682; files: internal/cmd/gmail_messages.go)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Add redacted terminal output from the current head showing indexed listing output and an indexed download; remove account data and opaque IDs.
  • After adding proof, update the PR body for re-review; if it does not refresh automatically, ask a maintainer to comment @clawsweeper re-review.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (22 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-08T13:04:51.989Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Emit indexed metadata in raw thread JSON | [P2] Expose indexed metadata in draft JSON | [P2] Validate indexed arguments before dry-run
  • reviewed 2026-08-08T14:56:34.159Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Expose indexed metadata in raw thread JSON | [P2] Emit attachment indexes in draft JSON | [P2] Validate indexed input before dry-run succeeds
  • reviewed 2026-08-08T16:04:57.079Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Transform raw thread JSON in indexed mode | [P2] Emit indexes in ordinary draft JSON | [P2] Validate indexed input before dry-run succeeds
  • reviewed 2026-08-08T17:22:38.194Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Transform raw thread JSON in indexed mode | [P2] Emit indexes in raw draft JSON | [P2] Validate indexed input before dry-run succeeds
  • reviewed 2026-08-08T18:41:39.066Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Emit indexes in raw thread JSON | [P2] Emit indexes in raw draft JSON | [P2] Validate the indexed argument before dry-run
  • reviewed 2026-08-08T20:15:07.972Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Honor indexed output in raw thread JSON | [P2] Emit indexes in raw draft JSON | [P2] Validate indexed arguments before dry-run
  • reviewed 2026-08-08T21:29:20.666Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Transform raw thread JSON in indexed mode | [P2] Expose an indexed draft reference without downloads | [P2] Validate indexed input before dry-run
  • reviewed 2026-08-08T23:21:22.617Z sha d63b32d :: needs real behavior proof before merge. :: [P2] Transform raw thread JSON in indexed mode | [P2] Expose indexed draft references without downloads | [P2] Validate indexed input before dry-run success

@ronny-rentner
ronny-rentner force-pushed the feat/gmail-attachment-by-index branch from 0350e79 to abfd1c6 Compare August 7, 2026 10:17
@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. labels Aug 7, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. and removed merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Aug 8, 2026
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. label Aug 9, 2026
@steipete
steipete merged commit 8b44d0a into openclaw:main Aug 9, 2026
8 checks passed
@steipete

steipete commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

Landed in 8b44d0a81f9f8fd06f76d0150539d824e1a11449; maintainer changelog follow-up is 8b33065cb.

Maintainer work before landing:

Proof:

  • GOCACHE=<lane-private-cache> make ci — passed the complete local gate.
  • Focused attachment, message-search, presentation, draft, and thread tests passed.
  • Signed current-head binary exercised against real Gmail with account/content identifiers redacted: default search emitted attachmentId; indexed search emitted attachmentIndex: 0; indexed message and thread JSON contained usable mappings and zero opaque-ID fields; gmail attachment <messageId> 0 downloaded 34,710 bytes successfully.
  • A clearly named temporary draft with one synthetic attachment returned index 0 and no opaque ID from indexed drafts get; it was deleted after proof. The first harness pass parsed the create response under the wrong key, so the cleanup trap missed it; that exact draft was immediately deleted before the corrected end-to-end rerun, whose cleanup also passed.
  • Final Codex autoreview: clean, no accepted/actionable findings.
  • GitHub CI run 31292547223 passed Linux, worker, Windows, and Darwin jobs; Docker run 31292547230 passed.

Thanks @ronny-rentner for the compact-reference design and persistent iteration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants