Skip to content

[superlog] Resolve domain name to UUID in annotation tools#480

Open
superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/resolve-annotation-website-domain
Open

[superlog] Resolve domain name to UUID in annotation tools#480
superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/resolve-annotation-website-domain

Conversation

@superlog-app

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

Copy link
Copy Markdown

Summary

The insights agent's create_annotation (and list_annotations) tool failed with NOT_FOUND: website not found whenever the AI passed a domain name (e.g. "finvzo.com") as websiteId instead of the site's UUID. The annotations RPC router stores and looks up websites by UUID, so domain names were never found.

The AI learns site domains from the ClickHouse query context and sometimes uses them where the UUID is expected. The execute_sql tool already handles this correctly (via resolveToolWebsite + withServerBoundIds), but the annotation tools did not call resolveToolWebsite at all.

Two-part fix:

  1. packages/ai/src/ai/tools/utils/context.ts — extend resolveToolWebsite to also match by domain name, returning the corresponding UUID from accessibleWebsites (or from ctx.websiteDomain/ctx.websiteId for single-site contexts). If no match is found by domain either, the existing error is thrown.
  2. packages/ai/src/ai/tools/annotations.ts — import and call resolveToolWebsite in both listAnnotationsTool and createAnnotationTool before passing websiteId to the RPC, mirroring the pattern already used in execute-sql-query.ts.

An alternative approach would be to add a Zod pre-process step to the tool's inputSchema that normalises the websiteId field, but fixing it at the resolveToolWebsite layer is consistent with the existing pattern and benefits all tools that call it.

Incident on Superlog


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


Summary by cubic

Fixes annotation tools failing when a website domain is provided instead of a UUID by resolving domains to UUIDs before calling the annotations RPC. Aligns behavior with execute_sql and removes "website not found" errors.

  • Bug Fixes
    • Extended resolveToolWebsite to accept domains and map them to UUIDs, including single-site contexts; unknown domains still error.
    • Applied resolveToolWebsite in listAnnotationsTool and createAnnotationTool so RPCs receive the resolved websiteId.
    • Added tests for domain-to-UUID resolution and unknown domain rejection.

Written for commit 403396d. Summary will update on new commits.

Review in cubic

@vercel

vercel Bot commented Jun 15, 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 15, 2026 9:17am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
dashboard Skipped Skipped Jun 15, 2026 9:17am
documentation Skipped Skipped Jun 15, 2026 9:17am

@unkey-deploy

unkey-deploy Bot commented Jun 15, 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 15, 2026 9:17am

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a NOT_FOUND: website not found error in the annotation tools by extending resolveToolWebsite with a domain-name fallback and calling it in listAnnotationsTool / createAnnotationTool before any RPC call — mirroring the pattern already used in the SQL tool.

  • context.ts: resolveToolWebsite now tries a UUID match first, then searches accessibleWebsites by domain, then falls back to ctx.websiteDomain before throwing. Three new unit tests cover all three branches.
  • annotations.ts: Both listAnnotationsTool and createAnnotationTool now resolve websiteId upfront; the resolved UUID is passed to the RPC and (in createAnnotationTool) to the logger.

Confidence Score: 4/5

The fix is targeted and correct — annotation tools now resolve domains to UUIDs before calling the RPC, matching the existing pattern in the SQL tool. The new test coverage is solid.

The core resolution logic works correctly and is well-tested. The domain comparison uses strict equality, which is case-sensitive; if the AI or stored data has mixed casing, the same NOT_FOUND failure could resurface. There is also a minor inconsistency where listAnnotationsTool's error logger still uses the raw websiteId input rather than the resolved UUID.

packages/ai/src/ai/tools/utils/context.ts — the case-insensitive domain comparison.

Important Files Changed

Filename Overview
packages/ai/src/ai/tools/utils/context.ts Adds domain-name fallback to resolveToolWebsite: tries UUID match first, then searches accessibleWebsites by domain, then checks ctx.websiteDomain. Logic is correct but domain comparison is case-sensitive (===), which could miss variations like uppercase domains from the AI.
packages/ai/src/ai/tools/annotations.ts Adds resolveToolWebsite calls to listAnnotationsTool and createAnnotationTool before RPC calls, but listAnnotationsTool's catch block still logs the raw websiteId rather than resolved.websiteId.
packages/ai/src/ai/tools/utils/context.test.ts Adds three new test cases covering the domain-resolution path: lookup from accessibleWebsites, lookup from ctx.websiteDomain, and rejection of an unknown domain. All scenarios are well-covered.

Sequence Diagram

sequenceDiagram
    participant AI as AI Agent
    participant AT as annotations.ts
    participant RW as resolveToolWebsite
    participant RPC as annotations RPC

    AI->>AT: "listAnnotations/createAnnotation(websiteId = finvzo.com)"
    AT->>RW: resolveToolWebsite(ctx, finvzo.com)
    RW->>RW: UUID match? no
    RW->>RW: "domain match in accessibleWebsites? yes -> web_uuid_123"
    RW-->>AT: "{ websiteId: web_uuid_123, domain: finvzo.com }"
    AT->>RPC: "list/create({ websiteId: web_uuid_123, ... })"
    RPC-->>AT: result
    AT-->>AI: success
Loading

Comments Outside Diff (1)

  1. packages/ai/src/ai/tools/annotations.ts, line 114-119 (link)

    P2 The catch block in listAnnotationsTool still logs the raw websiteId input (which may be a domain string), while createAnnotationTool's catch block was updated in this same PR to log resolved.websiteId. When a domain name is passed and the RPC call fails, the log entry will record the domain instead of the UUID that was actually sent to the server, making it harder to correlate with backend traces.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Reviews (1): Last reviewed commit: "[superlog] Resolve domain name to UUID i..." | Re-trigger Greptile

Comment on lines +40 to +42
const byDomain = accessible.find(
(w) => w.domain != null && w.domain === inputWebsiteId
);

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 Domain comparison uses strict equality (===), but hostnames are case-insensitive per RFC 1034. If the AI returns a domain like "Finvzo.COM" or the stored value has different casing than the input, the lookup silently falls through and throws the "not in this workspace" error — the same failure mode this PR is fixing. Normalising both sides to lowercase is the standard defensive approach.

Suggested change
const byDomain = accessible.find(
(w) => w.domain != null && w.domain === inputWebsiteId
);
const normalizedInput = inputWebsiteId.toLowerCase();
const byDomain = accessible.find(
(w) => w.domain != null && w.domain.toLowerCase() === normalizedInput
);

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