Skip to content

[superlog] Resolve websiteId in annotation tools to fix domain-name mismatch#474

Open
superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/fix-annotation-websiteid-resolution
Open

[superlog] Resolve websiteId in annotation tools to fix domain-name mismatch#474
superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/fix-annotation-websiteid-resolution

Conversation

@superlog-app

@superlog-app superlog-app Bot commented Jun 12, 2026

Copy link
Copy Markdown

Summary

The insights agent's create_annotation (and list_annotations) tool was passing the LLM's raw websiteId input directly to the annotations RPC. The LLM consistently passes the website domain name (e.g., "databuddy.cc") instead of the internal website ID (e.g., "OXmNQsViBT-FOS_wZCTHc"), because it sees domain names appear to work as websiteId in SQL queries — the execute_sql tool silently overrides the model's value server-side, but no such override existed in the annotation tools. The result is a NOT_FOUND error on every annotation creation attempt.

The root cause is two-part: (1) the annotation tools did not call resolveToolWebsite to obtain the real internal ID from context, unlike execute-sql-query.ts; and (2) resolveToolWebsite only matched by ID and would throw rather than fall back if the model happened to pass a domain name.

This PR adds resolveToolWebsite(context, websiteId) calls to createAnnotationTool and listAnnotationsTool, and extends resolveToolWebsite to also match a domain name against ctx.websiteDomain and accessibleWebsites[*].domain, returning the corresponding internal ID. All existing resolveToolWebsite tests continue to pass, and three new regression tests cover the domain-name matching paths.

An alternative approach would be to strip the websiteId input from the annotation tool schema entirely and always use ctx.websiteId — that would be simpler for the insights-agent-only case but would break multi-website chat contexts where the user explicitly picks a website. The current fix preserves that flexibility while fixing the domain-name confusion.

Incident on Superlog


Was this PR helpful? Leave feedback — goes straight to the Superlog team.


Summary by cubic

Fixes annotation tools so domain names like "databuddy.cc" resolve to the correct internal website ID. This removes NOT_FOUND errors when creating or listing annotations and keeps multi-website selection working.

  • Bug Fixes
    • Use website resolution in list/create annotation tools to map a domain or ID to the internal websiteId.
    • Extend website resolution to match by ctx.websiteDomain or accessibleWebsites[*].domain, with clear errors if not found.
    • Added regression tests for domain matching; existing tests continue to pass.

Written for commit e3c93fc. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
databuddy-status Ready Ready Preview, Comment Jun 12, 2026 3:58pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
dashboard Skipped Skipped Jun 12, 2026 3:58pm
documentation Skipped Skipped Jun 12, 2026 3:58pm

@unkey-deploy

unkey-deploy Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Unkey Deploy

Name Status Preview Inspect Updated (UTC)
api (preview) Ready Visit Preview Inspect Jun 12, 2026 3:59pm

@greptile-apps

greptile-apps Bot commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes annotation tools (create_annotation, list_annotations) that were sending raw LLM-supplied domain names to the annotations RPC, causing NOT_FOUND errors, by routing them through resolveToolWebsite and extending that function to also resolve inputs by domain name against the accessible-websites list and ctx.websiteDomain.

  • context.ts: Adds a two-step domain-name fallback in resolveToolWebsite — first checks accessibleWebsites[*].domain, then ctx.websiteDomain — before throwing; however the ctx.websiteDomain branch incorrectly prioritises ctx.defaultWebsiteId over ctx.websiteId, which can return the wrong internal ID.
  • annotations.ts: Wires resolveToolWebsite into listAnnotationsTool (inside try) and createAnnotationTool (outside try), the latter meaning resolution errors skip the catch-and-log handler.
  • context.test.ts: Adds three regression tests for the new domain-matching paths, all structurally correct.

Confidence Score: 3/5

The core fix is directionally correct but the new ctx.websiteDomain branch in resolveToolWebsite uses the wrong ID precedence, which can silently route an annotation to the wrong website in multi-website contexts.

The ctx.websiteDomain resolution path returns ctx.defaultWebsiteId ?? ctx.websiteId, but ctx.websiteDomain is the domain of ctx.websiteId, not ctx.defaultWebsiteId. In any session where those two IDs differ, domain-based resolution returns the default website's ID while labelling it with the current website's domain — an ID/domain mismatch that silently creates the annotation on the wrong property.

packages/ai/src/ai/tools/utils/context.ts — the ctx.websiteDomain fallback branch; the new regression tests do not exercise a case where defaultWebsiteId and websiteId differ simultaneously, so this bug is not caught by the test suite.

Important Files Changed

Filename Overview
packages/ai/src/ai/tools/utils/context.ts Adds domain-name fallback to resolveToolWebsite; has a priority bug where ctx.defaultWebsiteId is preferred over ctx.websiteId when matching by ctx.websiteDomain, potentially returning a mismatched ID/domain pair.
packages/ai/src/ai/tools/annotations.ts Adds resolveToolWebsite calls to listAnnotationsTool and createAnnotationTool; list tool places the call inside try/catch correctly, but create tool places it outside, so resolution failures skip error logging.
packages/ai/src/ai/tools/utils/context.test.ts Adds three regression tests for domain-name matching; covers ctx.websiteDomain match, accessible-list domain match, and the reject-unknown-domain path. Tests are correct and aligned with intended behavior.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["LLM passes websiteId e.g. 'databuddy.cc'"] --> B[resolveToolWebsite]
    B --> C{Exact ID match in accessible or ctx.websiteId?}
    C -- Yes --> D[Return internal ID + domain]
    C -- No --> E{Domain match in accessible list?}
    E -- Yes --> F[Return matched website ID]
    E -- No --> G{Matches ctx.websiteDomain?}
    G -- Yes --> H["Return ctx.defaultWebsiteId ?? ctx.websiteId ⚠️ Should prefer ctx.websiteId first"]
    G -- No --> I[Throw: not in workspace]
    D --> J[RPC call with correct internal ID]
    F --> J
    H --> J
Loading

Reviews (1): Last reviewed commit: "[superlog] Resolve websiteId in annotati..." | Re-trigger Greptile

Comment on lines +43 to +48
if (ctx.websiteDomain === inputWebsiteId) {
const fallbackId = ctx.defaultWebsiteId ?? ctx.websiteId;
if (fallbackId) {
return { websiteId: fallbackId, domain: ctx.websiteDomain };
}
}

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.

P1 Wrong ID priority when resolving by ctx.websiteDomain. ctx.websiteDomain is specifically the domain of ctx.websiteId, not of ctx.defaultWebsiteId. If a user's current context is website A (domain "databuddy.cc") but their default website is website B, passing "databuddy.cc" will incorrectly resolve to B while labelling it with A's domain — a silent ID/domain mismatch. Prefer ctx.websiteId over ctx.defaultWebsiteId in this branch.

Suggested change
if (ctx.websiteDomain === inputWebsiteId) {
const fallbackId = ctx.defaultWebsiteId ?? ctx.websiteId;
if (fallbackId) {
return { websiteId: fallbackId, domain: ctx.websiteDomain };
}
}
if (ctx.websiteDomain === inputWebsiteId) {
const fallbackId = ctx.websiteId ?? ctx.defaultWebsiteId;
if (fallbackId) {
return { websiteId: fallbackId, domain: ctx.websiteDomain };
}
}

Comment on lines 148 to 150
const context = getAppContext(options);
const resolved = resolveToolWebsite(context, websiteId);
try {

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 Resolution error escapes the catch handler in createAnnotationTool. resolveToolWebsite is called before the try block (unlike listAnnotationsTool where it is inside), so if resolution throws (e.g. domain not in workspace), the error propagates without being logged via logger.error. Moving the call into the try block would make error-handling consistent with listAnnotationsTool.

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.

0 participants