Skip to content

feat: at-mentions for canvas nodes#3968

Open
re-pixel wants to merge 9 commits intosuperplanehq:mainfrom
re-pixel:feat/at-mentions
Open

feat: at-mentions for canvas nodes#3968
re-pixel wants to merge 9 commits intosuperplanehq:mainfrom
re-pixel:feat/at-mentions

Conversation

@re-pixel
Copy link
Copy Markdown
Collaborator

@re-pixel re-pixel commented Apr 6, 2026

What this does

In AI Builder, users can @-mention steps on the canvas by the same names they see in the workflow. Mentions are chosen from a typeahead list, sent to the agent as stable node IDs (not guessed from free text), and the agent gets extra context for those nodes.

User-facing behavior

  1. Type @ in the composer to open a filtered list of canvas nodes (by display name).
  2. Pick a row (keyboard or mouse); the composer inserts @ + that name and a trailing space.
  3. On send, the client replaces those mentions with wire tokens @[node:<id>] in the request body.
  4. The agent parses those tokens, loads the canvas once via describe_canvas, and appends a short “Referenced nodes” section to the model prompt (id, name, type, block).
  5. Chat UI does not show that internal appendix or raw IDs in previews where we can resolve names.

That appendix increases the size of the text we send in the prompt, but it pays for itself in practice: the model no longer has to infer which node the user meant or discover it only by calling tools—the user’s selection is explicit, which removes one reasoning step and tends to make runs more direct.

Implementation

  • Wire format on send: The UI keeps human @Name text in the composer; right before fetch, transcodeAiNodeMentions() rewrites mentions to stable @[node:id] tokens (longest display name first, same boundaries as autocomplete).
  • Composer UX: @ opens a filtered node list; Enter picks a row when the menu is open, otherwise it sends; the menu is portaled with fixed positioning and a high z-index so it stacks above the rest of the sidebar; accepting a mention closes the menu and refreshes mention state from the cursor.
  • Readable history: formatAiBuilderWireMentionsForDisplay() turns @[node:id] back into @DisplayName using the current canvasNodes map; the sidebar applies that to session titles and user message text so lists and bubbles stay name-first.
  • Agent expansion: node_mentions.py parses wire tokens, resolves nodes via describe_canvas, appends the “Referenced nodes” appendix to the model prompt, and primes canvas_cache; repl_web runs that expansion on each user turn; system_prompt.txt documents the token convention.
  • UI vs model copy: The full expanded string (including the appendix) stays in stored model messages; strip_referenced_nodes_appendix_for_display() removes the appendix only when building user-facing history from the API; PersistedRunRecorder can take a separate initial_message so chat titles/previews stay the short client question.
  • Tests: test_node_mentions.py covers parsing, expansion, and stripping helpers.

Related Issues

#3966

Comment thread web_src/src/ui/BuildingBlocksSidebar/AiBuilderChatPanel.tsx Outdated
Copy link
Copy Markdown
Contributor

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e1e348f. Configure here.

const re = new RegExp(`(^|[\\s])@${escaped}(?=\\s|$|[.,!?;:])`, "g");
out = out.replace(re, `$1@[node:${id}]`);
}
return out;
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.

Duplicate display names cause wrong node transcoding

Medium Severity

transcodeAiNodeMentions matches @Name text by display name, but applyMention only inserts the display name (not the node ID) into the composer. When two or more nodes share the same aiBuilderNodeDisplayName (common for unnamed nodes, which all resolve to "Untitled"), every @Untitled mention silently maps to whichever node appears first — regardless of which entry the user explicitly picked from the typeahead. The user's deliberate selection is lost.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e1e348f. Configure here.

Comment thread agent/src/ai/node_mentions.py Outdated
@superplanehq-integration
Copy link
Copy Markdown

👋 Commands for maintainers:

  • /sp start - Start an ephemeral machine (takes ~30s)
  • /sp stop - Stop a running machine (auto-executed on pr close)

re-pixel added 9 commits April 6, 2026 13:26
Signed-off-by: re-pixel <relja.brdar@gmail.com>
…u disappear when the mention is selected

Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
Signed-off-by: re-pixel <relja.brdar@gmail.com>
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.

1 participant