Skip to content

feat(discord): extract embed fields and footer for agent visibility#53288

Open
jeremyknows wants to merge 2 commits into
openclaw:mainfrom
jeremyknows:feat/discord-embed-full-text
Open

feat(discord): extract embed fields and footer for agent visibility#53288
jeremyknows wants to merge 2 commits into
openclaw:mainfrom
jeremyknows:feat/discord-embed-full-text

Conversation

@jeremyknows
Copy link
Copy Markdown
Contributor

Summary

  • Expand resolveDiscordEmbedText to extract title, description, fields, and footer (previously only title and description)
  • Iterate all embeds in a message, not just embeds[0], at all three call sites
  • Export DiscordEmbedLike type for cross-file reuse

Motivation

Many Discord bot messages use embeds for structured content — status dashboards, alerts, monitoring cards, cross-agent communication. When message.content is empty and data lives in embed fields/footer, agents currently see nothing. This is especially impactful in multi-agent setups where bots communicate primarily through rich embeds.

Changes

extensions/discord/src/monitor/message-utils.ts:

  • New exported DiscordEmbedLike type with title, description, fields, and footer
  • resolveDiscordEmbedText() rewritten to extract all embed parts, joining fields as Name: Value
  • resolveDiscordMessageText() and resolveDiscordSnapshotMessageText() iterate all embeds with --- separator

extensions/discord/src/monitor/threading.ts:

  • Thread starter embed extraction updated to use DiscordEmbedLike and iterate all embeds

extensions/discord/src/monitor/message-utils.test.ts:

  • 5 new test cases: fields-only, footer with title, combined (title+description+fields+footer), multiple embeds with separator, whitespace-only field filtering

Design decisions

  • Fallback chain unchanged: embed text is only used when message.content is empty — no change for messages that already have text content
  • Fields formatted as Name: Value: simple, readable, no parsing overhead for agents
  • --- separator between embeds: distinguishes multi-embed boundaries
  • Whitespace-only fields skipped: prevents blank entries from cluttering agent context
  • author.name not included: intentionally left for a follow-up — keeps this PR minimal

Known limitation

When a message has both text content AND embeds, only the content is shown (embed text is fallback-only). This is existing behavior and intentional — changing it would require a larger design discussion about how to present both without context bloat.

Test plan

  • All 34 tests pass (29 existing + 5 new)
  • tsgo --noEmit clean
  • Full pre-commit check suite passed (format, lint, type-check, boundaries, imports)
  • Live-tested on production gateway: Watson confirmed title, description, all fields, and footer visible in agent context from embed-only messages

🤖 Generated with Claude Code

…Text

Previously, `resolveDiscordEmbedText` only extracted title and description
from the first embed. Many bot messages (status dashboards, alerts, moderation
logs, cross-agent communication) put critical information in embed fields
and footer, and may include multiple embeds per message.

Changes:
- Expand `resolveDiscordEmbedText` to extract title, description, fields
  (as "Name: Value"), and footer text
- Extract from all embeds (not just embeds[0]) at all three call sites:
  resolveDiscordMessageText, snapshot resolution, and thread starter
- Add `DiscordEmbedLike` type and export it for cross-file use
- Add 5 test cases covering fields, footer, combined extraction,
  multiple embeds, and whitespace-only field filtering

The embed text fallback chain is unchanged: agent sees message content
first, then media placeholder, then embed text. Embed extraction only
activates when message content is empty.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openclaw-barnacle openclaw-barnacle Bot added channel: discord Channel integration: discord size: S labels Mar 24, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 24, 2026

Greptile Summary

This PR expands Discord embed extraction so agents can see fields and footers (not just title/description), and processes all embeds in a message rather than only the first one. The changes are focused, well-tested, and consistent across all three call sites (resolveDiscordMessageText, resolveDiscordSnapshotMessageText, and resolveDiscordThreadStarter).

Key changes:

  • DiscordEmbedLike type is exported and reused everywhere, eliminating repeated inline type literals.
  • resolveDiscordEmbedText now collects title, description, fields (formatted as Name: Value), and footer text, joining with \n. Whitespace-only parts are skipped.
  • All three call sites switch from embeds?.[0] to iterating the full array, joining non-empty embed texts with \n---\n.
  • Five new tests cover the added paths; existing 29 tests continue to pass.
  • One minor gap: the test titled "skips fields with whitespace-only name or value" only covers whitespace name — the value branch is exercised by the implementation but not by the test suite.

Confidence Score: 5/5

  • Safe to merge — changes are additive, well-tested, and backwards-compatible with existing content-based messages.
  • The fallback chain is unchanged (embed text is only used when message.content is empty), so existing behavior for messages with content is untouched. The new paths are straightforward data extraction with proper null-guarding throughout. The single P2 comment is a missing test variant for whitespace-only value, which doesn't indicate any implementation defect.
  • No files require special attention.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: extensions/discord/src/monitor/message-utils.test.ts
Line: 723-740

Comment:
**Test title claims "name or value" but only covers whitespace `name`**

The test description says "skips fields with whitespace-only name **or** value", but only a whitespace-only `name` is exercised. The implementation correctly short-circuits on either (`f.name?.trim() && f.value?.trim()`), so there's no bug — but the complementary case (whitespace-only `value`) isn't covered. Consider adding a second field to keep the promise made by the test name:

```suggestion
  it("skips fields with whitespace-only name or value", () => {
    const text = resolveDiscordMessageText(
      asMessage({
        content: "",
        embeds: [
          {
            title: "Test",
            fields: [
              { name: "  ", value: "val" },
              { name: "Real", value: "   " },
              { name: "Real", value: "data" },
            ],
          },
        ],
      }),
    );

    expect(text).toBe("Test\nReal: data");
  });
```

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat(discord): extract embed fields and ..." | Re-trigger Greptile

Comment on lines +723 to +740
it("skips fields with whitespace-only name or value", () => {
const text = resolveDiscordMessageText(
asMessage({
content: "",
embeds: [
{
title: "Test",
fields: [
{ name: " ", value: "val" },
{ name: "Real", value: "data" },
],
},
],
}),
);

expect(text).toBe("Test\nReal: data");
});
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.

P2 Test title claims "name or value" but only covers whitespace name

The test description says "skips fields with whitespace-only name or value", but only a whitespace-only name is exercised. The implementation correctly short-circuits on either (f.name?.trim() && f.value?.trim()), so there's no bug — but the complementary case (whitespace-only value) isn't covered. Consider adding a second field to keep the promise made by the test name:

Suggested change
it("skips fields with whitespace-only name or value", () => {
const text = resolveDiscordMessageText(
asMessage({
content: "",
embeds: [
{
title: "Test",
fields: [
{ name: " ", value: "val" },
{ name: "Real", value: "data" },
],
},
],
}),
);
expect(text).toBe("Test\nReal: data");
});
it("skips fields with whitespace-only name or value", () => {
const text = resolveDiscordMessageText(
asMessage({
content: "",
embeds: [
{
title: "Test",
fields: [
{ name: " ", value: "val" },
{ name: "Real", value: " " },
{ name: "Real", value: "data" },
],
},
],
}),
);
expect(text).toBe("Test\nReal: data");
});
Prompt To Fix With AI
This is a comment left during a code review.
Path: extensions/discord/src/monitor/message-utils.test.ts
Line: 723-740

Comment:
**Test title claims "name or value" but only covers whitespace `name`**

The test description says "skips fields with whitespace-only name **or** value", but only a whitespace-only `name` is exercised. The implementation correctly short-circuits on either (`f.name?.trim() && f.value?.trim()`), so there's no bug — but the complementary case (whitespace-only `value`) isn't covered. Consider adding a second field to keep the promise made by the test name:

```suggestion
  it("skips fields with whitespace-only name or value", () => {
    const text = resolveDiscordMessageText(
      asMessage({
        content: "",
        embeds: [
          {
            title: "Test",
            fields: [
              { name: "  ", value: "val" },
              { name: "Real", value: "   " },
              { name: "Real", value: "data" },
            ],
          },
        ],
      }),
    );

    expect(text).toBe("Test\nReal: data");
  });
```

How can I resolve this? If you propose a fix, please make it concise.

…test

Addresses Greptile review feedback: the test title promised coverage
for whitespace-only name "or value" but only exercised the name case.
Added a field with whitespace-only value to confirm both branches of
the trim guard are tested.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@openclaw-barnacle
Copy link
Copy Markdown

This pull request has been automatically marked as stale due to inactivity.
Please add updates or it will be closed.

@openclaw-barnacle openclaw-barnacle Bot added the stale Marked as stale due to inactivity label Apr 29, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 29, 2026

Codex review: needs real behavior proof before merge.

Summary
The PR expands Discord empty-content fallback text extraction to include embed fields, footer text, and all embeds for normal messages, forwarded snapshots, and thread starters, with focused tests.

Reproducibility: yes. By source inspection, current main and v2026.5.7 only normalize title/description from embeds?.[0] for normal messages, forwarded snapshots, and thread starters; a fields-only, footer-only, or multi-embed empty-content test would show the missing agent context.

Real behavior proof
Needs real behavior proof before merge: The PR body claims production gateway validation, but no inspectable after-fix artifact is attached; the contributor should add redacted proof such as a screenshot, recording, terminal output, linked artifact, or logs, update the PR body for re-review, and ask a maintainer to comment @clawsweeper re-review if it does not rerun automatically.

Next step before merge
Contributor and maintainer action is needed because the external PR lacks inspectable real behavior proof and must be refreshed onto the current Discord monitor split before normal validation.

Security
Cleared: The diff is limited to Discord monitor text extraction and tests, with no workflow, dependency, lockfile, package metadata, permission, or secret-handling changes.

Review findings

  • [P1] Replay embed extraction onto the split monitor helpers — extensions/discord/src/monitor/message-utils.ts:497-536
Review details

Best possible solution:

Replay the extraction onto the current message-text.ts, message-forwarded.ts, and threading.starter.ts helpers while preserving content/media/embed/Components v2 precedence, then add focused tests and redacted real Discord proof.

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

Yes. By source inspection, current main and v2026.5.7 only normalize title/description from embeds?.[0] for normal messages, forwarded snapshots, and thread starters; a fields-only, footer-only, or multi-embed empty-content test would show the missing agent context.

Is this the best way to solve the issue?

No, not as this branch currently lands. The behavior direction is narrow and maintainable, but the patch must be replayed onto the current split helpers and preserve newer Components v2 fallback behavior.

Full review comments:

  • [P1] Replay embed extraction onto the split monitor helpers — extensions/discord/src/monitor/message-utils.ts:497-536
    Current main moved this logic from the older monolithic files into message-text.ts, message-forwarded.ts, and threading.starter.ts, and added Components v2 fallback. Reapply the field/footer/all-embeds extraction to those current helpers so this can land without dropping newer fallback behavior.
    Confidence: 0.92

Overall correctness: patch is incorrect
Overall confidence: 0.92

Acceptance criteria:

  • pnpm test extensions/discord/src/monitor/message-utils.test.ts extensions/discord/src/monitor/threading.starter.test.ts
  • pnpm check:changed

What I checked:

  • Current normal-message fallback is first-embed title/description only: Current main normalizes only embed title and description, then passes only message.embeds?.[0]; fields, footer text, and later embeds are not surfaced. (extensions/discord/src/monitor/message-text.ts:15, 48fb4bade84c)
  • Forwarded snapshot fallback is also first-embed title/description only: Forwarded snapshots use resolveDiscordEmbedText(snapshot.embeds?.[0]), so snapshot fields, footer text, and additional embeds remain invisible. (extensions/discord/src/monitor/message-text.ts:162, 48fb4bade84c)
  • Thread starter fallback still reads one embed: Thread starter text uses content, then resolveDiscordEmbedText(starter.embeds?.[0]), then forwarded text; it has no fields/footer/all-embeds handling. (extensions/discord/src/monitor/threading.starter.ts:186, 48fb4bade84c)
  • Current tests cover the old embed behavior: The current Discord monitor tests cover title, description, title+description, content precedence, Components v2, and forwarded snapshot title/description, but not fields, footer text, or multiple embeds. (extensions/discord/src/monitor/message-utils.test.ts:1002, 48fb4bade84c)
  • PR diff implements the intended extraction on stale paths: The PR adds a DiscordEmbedLike type, extracts title/description/fields/footer, joins all non-empty embed texts with a separator, and updates tests, but it patches the older monolithic message-utils.ts and threading.ts layout. (extensions/discord/src/monitor/message-utils.ts:497, 987c0ffa6172)
  • Live PR state is not merge-ready: GitHub reports the PR head as mergeable=false / mergeStateStatus=DIRTY; the check rollup includes failed test checks, and the body/comments contain a live-test claim but no inspectable proof artifact. (987c0ffa6172)

Likely related people:

  • steipete: Recent GitHub path history shows the current split Discord monitor helpers and Components v2 fallback were introduced or maintained in commits touching the same runtime paths. (role: recent area contributor; confidence: high; commits: 43b084e5fa2e, 7e1acf2f1ee5, 62b20e7fa2ed; files: extensions/discord/src/monitor/message-text.ts, extensions/discord/src/monitor/message-forwarded.ts, extensions/discord/src/monitor/threading.starter.ts)
  • superman32432432: Authored the merged commit that introduced the original Discord embed title/description fallback helper and regression coverage before this PR's fields/footer extension. (role: introduced related behavior; confidence: medium; commits: 39cc547f74f1; files: src/discord/monitor/message-utils.ts, src/discord/monitor/message-utils.test.ts)
  • shakkernerd: Authored the merged commit that exported the embed fallback helper and aligned thread-starter parsing/tests with embed fallback behavior. (role: introduced related thread-starter behavior; confidence: medium; commits: a0a229a3bb92; files: src/discord/monitor/message-utils.ts, src/discord/monitor/threading.ts, src/discord/monitor/threading.starter.test.ts)

Remaining risk / open question:

  • The branch is dirty/conflicting against current main and patches files that no longer own the affected logic.
  • The PR body claims live production testing, but no redacted screenshot, recording, terminal output, linked artifact, or log is attached for review.
  • Fresh focused Discord tests and changed checks are needed after the branch is refreshed because the available PR check rollup includes failures.
  • The change intentionally exposes more Discord-controlled embed text to agents after normal channel gating, which maintainers should accept as part of the feature review.

Codex review notes: model gpt-5.5, reasoning high; reviewed against 48fb4bade84c.

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented Apr 29, 2026

Codex review: needs maintainer review before merge.

What this changes:

The PR expands Discord inbound fallback text extraction to include embed fields, footer text, and all embeds for normal messages, forwarded snapshots, and thread starters, with focused monitor tests.

Maintainer follow-up before merge:

This is a narrow, useful open implementation PR, but the remaining action is maintainer review, branch refresh, and normal validation rather than an autonomous replacement or repair job.

Review details

Best possible solution:

Discord fallback text should preserve existing precedence while exposing title, description, nonblank fields, footer text, and all embeds for empty-content normal messages, forwarded snapshots, and thread starters, with refreshed tests on the current Discord monitor implementation.

Acceptance criteria:

  • pnpm test extensions/discord/src/monitor/message-utils.test.ts extensions/discord/src/monitor/threading.starter.test.ts
  • pnpm check:changed

What I checked:

  • Current main embed helper is title/description-only: resolveDiscordEmbedText on current main normalizes only title and description; it has no field, footer, URL, or multi-embed handling. (extensions/discord/src/monitor/message-utils.ts:561, e46dccb35374)
  • Current main reads only the first embed at all affected call sites: Normal message fallback passes message.embeds?.[0], forwarded snapshot fallback passes snapshot.embeds?.[0], and thread starter fallback passes starter.embeds?.[0]. (extensions/discord/src/monitor/message-utils.ts:576, e46dccb35374)
  • Current tests cover old behavior only: Existing Discord monitor tests cover embed title, description, title+description, content precedence, and forwarded snapshot title+description, but not fields, footer text, or multiple embeds. (extensions/discord/src/monitor/message-utils.test.ts:972, e46dccb35374)
  • PR patch implements the requested behavior: The PR patch adds DiscordEmbedLike, extracts title/description/fields/footer, joins all nonempty embed texts with \n---\n, updates normal message, snapshot, and thread starter call sites, and adds tests. The second PR commit addresses Greptile's whitespace-only value test feedback. (extensions/discord/src/monitor/message-utils.ts:497, 987c0ffa6172)
  • Discord model already exposes the requested data: The local Discord Message wrapper exposes raw APIEmbed[], and the bundled Embed wrapper serializes both footer and fields, so the PR is surfacing already-modeled Discord embed data. (extensions/discord/src/internal/structures.ts:190, e46dccb35374)
  • Security-sensitive surface review: The PR patch touches only Discord monitor source and tests; it does not modify workflows, dependencies, lockfiles, install/build/release scripts, package metadata, permissions, or secret handling. The main security-relevant effect is intentional: more Discord-controlled embed text becomes visible to agents after normal channel gating. (987c0ffa6172)

Likely related people:

  • steipete: Recent GitHub path history and current blame for the central Discord monitor files are dominated by Discord internalization, boundary, and trimmed-reader refactors touching the same code paths. (role: recent maintainer / likely follow-up owner; confidence: high; commits: da6135d34c11, f0adbd48e8bc, c7347a492ee2; files: extensions/discord/src/monitor/message-utils.ts, extensions/discord/src/monitor/threading.ts, extensions/discord/src/monitor/message-utils.test.ts)
  • superman32432432: Authored the merged commit that added the original Discord embed title fallback helper and message-utils regression coverage before the Discord implementation was moved under extensions/. (role: introduced related behavior; confidence: medium; commits: 1bce4d64a337; files: src/discord/monitor/message-utils.ts, src/discord/monitor/message-utils.test.ts)
  • shakkernerd: Authored the merged commit that aligned thread starter parsing with embed fallback behavior and added the original thread starter tests. (role: introduced related thread-starter behavior; confidence: medium; commits: 49c97467e247, d78d98586f32; files: src/discord/monitor/threading.ts, src/discord/monitor/threading.starter.test.ts, src/discord/monitor/message-utils.ts)
  • artwalker: Recently repaired Discord forwarded snapshot/reference message text helpers in message-utils.ts, which overlap one of this PR's affected fallback paths. (role: adjacent owner; confidence: medium; commits: c5493b15d6f5, 60dc6a22c91f; files: extensions/discord/src/monitor/message-utils.ts)

Remaining risk / open question:

  • The PR branch is dirty against current main and likely needs a rebase or equivalent refresh around the recent Discord internalization and string-normalization changes.
  • The available PR-head CI is not green because extension-fast (discord) failed; fresh focused tests and the changed gate are still needed after refresh.
  • The behavior intentionally exposes more untrusted Discord embed content to agents. That matches the feature goal, but maintainers should accept the context-expansion tradeoff before landing.

Codex review notes: model gpt-5.5, reasoning high; reviewed against e46dccb35374.

@openclaw-barnacle openclaw-barnacle Bot removed the stale Marked as stale due to inactivity label Apr 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

channel: discord Channel integration: discord size: S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant