fix: staging bug sweep batch 2 — messaging, payments, activity, bounty, i18n, social, moonpay#137
fix: staging bug sweep batch 2 — messaging, payments, activity, bounty, i18n, social, moonpay#137oxoxDev wants to merge 14 commits into
Conversation
Activity rows showed only the actor for any kind the describe() switch didn't list — notably the backend's `ledger.<TYPE>` fallback kinds and the canonical group.fee / arbitration.fee / fee / event.refund kinds — because they hit the bare-actor default. Extract one shared formatter used by both the Activity list and the header marquee: normalize `ledger.<TYPE>` to its canonical kind, add the missing cases, and humanize unknown kinds so the feed always says what happened, not just who.
The X social link used an x.com/intent/follow popup URL; point it at the canonical https://x.com/tinyhumansai profile instead.
Posting a bounty escrows real funds, but the budget was a free-text input sent unvalidated, so a non-numeric or empty amount produced a confusing escrow failure. Make the budget a numeric input and validate title + positive amount (and a present wallet) before mutating, surfacing the reason inline; send a normalized amount string.
The MoonPay hosted redirect and the embedded off-ramp sell widget opened a cold DNS/TLS/TCP connection only when reached, adding round-trip latency before any bytes loaded. Preconnect the buy/sell origins (and dns-prefetch the sandbox hosts) from the document head so the connection is warm.
Searching for a peer by messaging key was non-deterministic: a base64 key whose characters were base58-safe was misclassified as a cryptoId and 404'd on directory.getAgent. Classify input unambiguously — handle, then base64 raw key, then base58 cryptoId. And stop resolveEncryptionAddress from silently falling back to the wallet identity key (which has no Signal key bundle, so the send 404s later); throw an actionable error at resolve time.
The inbox fell back to the wallet cryptoId as the directory-as actor when no @handle had resolved yet, so the backend rejected it (401/403/404) and the UI showed "failed to load inbox". Use the agent-auth path when there's no handle, hold the query until owned-identities settles, and treat 401/403/404 alike as the actionable connect-a-handle state.
|
@oxoxDev must be a member of the Vezures team on Vercel to deploy. Learn more about collaboration on Vercel and other options here. |
📝 WalkthroughWalkthroughThe PR tightens ChangesEncryption key strictness and error handling
Shared activity-description module extraction
Inbox query gating and error handling
Form validation, layout hints, and cluster-based configuration
Sidebar and settings internationalization
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8c6b1c7ae4
ℹ️ About Codex in GitHub
Codex has been enabled to automatically 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 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ? advertised | ||
| : card.publicKey; | ||
| if (!address) { | ||
| if (typeof advertised !== "string" || advertised.length === 0) { |
There was a problem hiding this comment.
Preserve the publicKey fallback for CLI messaging
When the peer is an SDK/CLI agent that has run tinyplace init/client.enableEncryption() but has not called the website-only publishEncryptionKey, its Signal bundle is still published at signer.publicKeyBase64 while its directory card only exposes publicKey. Rejecting such cards here makes website handle/cryptoId DM resolution and group key handoff fail before trying a bundle that exists; either keep card.publicKey as the fallback for these single-key agents or make SDK/CLI onboarding advertise metadata.encryptionPublicKey too.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Good catch — fixed in f59c831. resolveEncryptionAddress now falls back to card.publicKey when no encryptionPublicKey metadata is advertised, so SDK/CLI agents (bundle published under signer.publicKeyBase64 === publicKey, metadata never set) stay addressable for DMs and group key handoff. To preserve the original guard against website agents that never enabled encryption, the actionable "hasn't enabled encrypted messaging" error moved to send time: encryptEnvelope maps the bootstrap keys.getBundle 404 to it. So a real bundle is reachable, and a missing one still fails loudly instead of leaking a raw 404. Throw at resolve only when a card has neither key.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
website/src/components/explore/Activity.tsx (1)
22-39:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd explicit return type annotation to
relativeTime.As per coding guidelines, all functions in TypeScript files must have explicit return types.
📝 Proposed fix
-function relativeTime(timestamp: string): string { +function relativeTime(timestamp: string): string {Wait, I see the return type is already there
: string. Let me check the actual code again...Looking at line 22, I see:
function relativeTime(timestamp: string): string {This already has the explicit return type. So this is actually compliant. My mistake.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/components/explore/Activity.tsx` around lines 22 - 39, No fix is needed. The relativeTime function already has the explicit return type annotation `: string` declared on the function signature, which is compliant with the TypeScript coding guidelines. The code is correct as-is.Source: Coding guidelines
website/src/components/explore/Inbox.tsx (1)
93-112:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winUpdate the auth-state copy to mention handles.
This branch now intentionally covers 401/403/404 cases where the wallet may be connected but no usable handle exists. The current text only says “Connect your wallet,” which can leave connected users without the actionable next step.
Proposed copy tweak
- Connect your wallet to view your inbox + Connect your wallet and claim or select a handle to view your inbox🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/components/explore/Inbox.tsx` around lines 93 - 112, The copy text "Connect your wallet to view your inbox" in the isAuthError conditional block needs to be updated to account for cases where a wallet is connected but no valid handle exists. Update the text content in the paragraph element (the one with className containing "text-sm") to mention both connecting a wallet AND owning or creating a handle as necessary steps, making it clear to users that both are required to view the inbox.
🧹 Nitpick comments (4)
website/src/components/explore/jobs/Jobs.tsx (1)
205-240: ⚡ Quick winExtract
submitvalidation into a helper to satisfy the 20-line function limit.
submitnow exceeds the repository’s max-function-length rule for TS/JS files. Please split validation/parsing into a small helper (e.g., return{ parsedAmount, trimmedTitle } | error) and keepsubmitfocused on orchestration.Refactor sketch
+type PostJobValidationResult = + | { ok: true; trimmedTitle: string; parsedAmount: number } + | { ok: false; message: string }; + +const validatePostJobInput = ({ + title, + amount, + agentId, +}: { + title: string; + amount: string; + agentId: string | null; +}): PostJobValidationResult => { + const trimmedTitle = title.trim(); + if (!trimmedTitle) return { ok: false, message: "Enter a title for the bounty." }; + const parsedAmount = Number(amount.trim()); + if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) { + return { ok: false, message: "Enter a budget greater than 0." }; + } + if (!agentId) return { ok: false, message: "Connect your wallet to post a bounty." }; + return { ok: true, trimmedTitle, parsedAmount }; +};🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/components/explore/jobs/Jobs.tsx` around lines 205 - 240, The submit function exceeds the repository's max-function-length rule because it contains multiple validation checks. Extract the validation and parsing logic into a separate helper function that validates the trimmed title, parses the amount, and checks the agentId, returning either an object with the parsed values (parsedAmount, trimmedTitle) or an error message. Then modify the submit function to call this helper and only proceed with the create.mutate call if validation succeeds, keeping submit focused purely on orchestration and mutation execution.Source: Coding guidelines
website/src/common/activity-describe.ts (2)
82-127: ⚡ Quick winEnhance JSDoc with parameter and return value documentation.
The exported
describeActivityfunction has a JSDoc comment but is missing@paramand@returnstags as per TypeScript documentation best practices.📝 Enhanced JSDoc
/** * A human-readable description of an activity event — the action performed, not * just who performed it. Unknown kinds humanize their name instead of collapsing * to a bare actor, so the feed always says what happened. + * + * `@param` event - The activity event to describe + * `@returns` A human-readable sentence describing the activity */ export function describeActivity(event: ActivityEvent): string {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/common/activity-describe.ts` around lines 82 - 127, The JSDoc comment for the describeActivity function is missing TypeScript documentation best practices. Add a `@param` tag to document the event parameter of type ActivityEvent and add a `@returns` tag to document that the function returns a string. Update the existing JSDoc block that currently only contains a description to include these tags documenting the function's parameters and return type.Source: Coding guidelines
54-57: ⚡ Quick winAdd JSDoc comment for exported function.
As per coding guidelines, all public functions and exported modules must include JSDoc comments. While there's a brief inline comment above, a proper JSDoc block would improve API documentation.
📝 Proposed JSDoc addition
-/** An emoji for an activity kind (resolves `ledger.<TYPE>` fallbacks too). */ +/** + * Returns an emoji icon for the given activity kind. + * + * Normalizes `ledger.<TYPE>` fallback kinds to their canonical activity kind + * before looking up the icon. Returns a bullet point (•) for unknown kinds. + * + * `@param` kind - The activity kind (e.g., "marketplace.purchase", "ledger.SALE") + * `@returns` An emoji string representing the activity type + */ export function activityIcon(kind: string): string {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/common/activity-describe.ts` around lines 54 - 57, The exported function activityIcon needs a proper JSDoc comment block instead of the existing inline comment to comply with coding guidelines. Replace the current comment with a JSDoc block that includes a description of what the function does, a `@param` tag documenting the kind parameter and its purpose, and a `@returns` tag describing the returned emoji string. Preserve the existing information about emoji resolution and ledger fallback behavior in the description.Source: Coding guidelines
website/src/hooks/use-inbox.ts (1)
35-39: ⚡ Quick winDocument the new public
enabledoption.
useInboxis an exported hook with a changed public signature, but the newoptions.enabledcontract is only described inside the implementation. Add JSDoc so callers know it is for deferring queries until the inbox owner is resolved.Proposed documentation
+/** + * Loads inbox items for the resolved owner. + * + * Pass `options.enabled: false` to defer the query while the inbox owner is + * still resolving; when omitted, the query runs immediately. + */ export function useInbox( parameters?: InboxQueryParams, owner?: string, options?: { enabled?: boolean }As per coding guidelines, “Always include JSDoc comments for public functions and exported modules.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/hooks/use-inbox.ts` around lines 35 - 39, The exported useInbox function is missing JSDoc documentation for its public signature, particularly the new options.enabled property. Add JSDoc comments above the useInbox function that document all parameters including the InboxQueryParams, owner string, and options object, with specific documentation for the enabled property explaining it defers query execution until the inbox owner is resolved. Also document the return type UseQueryResult<InboxListResult>.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/src/components/explore/Inbox.tsx`:
- Around line 76-82: The inbox mutations for actions like read, archive, and
delete are not gated by the same ownedIdentities.isLoading check that controls
the query, allowing them to execute with an undefined owner while the handle
resolves. Add the enabled condition with !ownedIdentities.isLoading to all
mutation calls in the Inbox component (including those at the line ranges
mentioned in the comment) to prevent mutations from firing until the
owned-identities lookup settles and owner is properly defined.
In `@website/src/components/explore/jobs/Jobs.tsx`:
- Around line 206-223: The button's disabled state logic does not match the
validation rules applied in the submit handler, causing the button to appear
enabled for invalid inputs like whitespace-only titles or non-positive amounts
until the user clicks and the submit validation fails. Update the disabled
condition on the submit button to use the same normalized validation checks as
the submit function: validate the trimmed title is not empty, validate the
parsed amount is finite and greater than 0, and check that agentId exists. This
ensures the button is only enabled when all validation conditions in the submit
handler would pass.
In `@website/src/hooks/use-direct-messages.ts`:
- Around line 31-36: The regex pattern BASE64_ENCRYPTION_KEY_PATTERN only
matches padded base64 keys ending with `=`, but unpadded 43-character base64
keys (without the `=`) are also valid messaging keys that should be caught
before the cryptoId pattern check. Update the BASE64_ENCRYPTION_KEY_PATTERN
regex to make the trailing `=` optional by using a quantifier, so it matches
both padded keys (43 chars + `=`) and unpadded keys (43 chars without `=`),
preventing valid messaging keys from being misclassified as cryptoIds.
---
Outside diff comments:
In `@website/src/components/explore/Activity.tsx`:
- Around line 22-39: No fix is needed. The relativeTime function already has the
explicit return type annotation `: string` declared on the function signature,
which is compliant with the TypeScript coding guidelines. The code is correct
as-is.
In `@website/src/components/explore/Inbox.tsx`:
- Around line 93-112: The copy text "Connect your wallet to view your inbox" in
the isAuthError conditional block needs to be updated to account for cases where
a wallet is connected but no valid handle exists. Update the text content in the
paragraph element (the one with className containing "text-sm") to mention both
connecting a wallet AND owning or creating a handle as necessary steps, making
it clear to users that both are required to view the inbox.
---
Nitpick comments:
In `@website/src/common/activity-describe.ts`:
- Around line 82-127: The JSDoc comment for the describeActivity function is
missing TypeScript documentation best practices. Add a `@param` tag to document
the event parameter of type ActivityEvent and add a `@returns` tag to document
that the function returns a string. Update the existing JSDoc block that
currently only contains a description to include these tags documenting the
function's parameters and return type.
- Around line 54-57: The exported function activityIcon needs a proper JSDoc
comment block instead of the existing inline comment to comply with coding
guidelines. Replace the current comment with a JSDoc block that includes a
description of what the function does, a `@param` tag documenting the kind
parameter and its purpose, and a `@returns` tag describing the returned emoji
string. Preserve the existing information about emoji resolution and ledger
fallback behavior in the description.
In `@website/src/components/explore/jobs/Jobs.tsx`:
- Around line 205-240: The submit function exceeds the repository's
max-function-length rule because it contains multiple validation checks. Extract
the validation and parsing logic into a separate helper function that validates
the trimmed title, parses the amount, and checks the agentId, returning either
an object with the parsed values (parsedAmount, trimmedTitle) or an error
message. Then modify the submit function to call this helper and only proceed
with the create.mutate call if validation succeeds, keeping submit focused
purely on orchestration and mutation execution.
In `@website/src/hooks/use-inbox.ts`:
- Around line 35-39: The exported useInbox function is missing JSDoc
documentation for its public signature, particularly the new options.enabled
property. Add JSDoc comments above the useInbox function that document all
parameters including the InboxQueryParams, owner string, and options object,
with specific documentation for the enabled property explaining it defers query
execution until the inbox owner is resolved. Also document the return type
UseQueryResult<InboxListResult>.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4c4c59ba-68b7-49f2-9181-1914f349ffaa
📒 Files selected for processing (12)
sdk/typescript/src/messaging/discovery.tssdk/typescript/tests/encryption-discovery.test.tswebsite/app/layout.tsxwebsite/src/common/activity-describe.test.tswebsite/src/common/activity-describe.tswebsite/src/components/ActivityMarquee.tsxwebsite/src/components/explore/Activity.tsxwebsite/src/components/explore/Inbox.tsxwebsite/src/components/explore/jobs/Jobs.tsxwebsite/src/components/layout/Sidebar.tsxwebsite/src/hooks/use-direct-messages.tswebsite/src/hooks/use-inbox.ts
| // The budget is escrowed on post, so a malformed amount must never reach | ||
| // the mutation — validate it client-side and surface the reason. | ||
| const trimmedTitle = title.trim(); | ||
| if (!trimmedTitle) { | ||
| setFormError("Enter a title for the bounty."); | ||
| return; | ||
| } | ||
| const parsedAmount = Number(amount.trim()); | ||
| if (!Number.isFinite(parsedAmount) || parsedAmount <= 0) { | ||
| setFormError("Enter a budget greater than 0."); | ||
| return; | ||
| } | ||
| if (!agentId) { | ||
| setFormError("Connect your wallet to post a bounty."); | ||
| return; | ||
| } | ||
| setFormError(null); | ||
| create.mutate( |
There was a problem hiding this comment.
Align button disabled logic with the same normalized validation rules used in submit.
The button can be enabled for whitespace titles or non-positive numeric strings, then fails only after click. Use trimmed title + parsed positive amount for disabled so the UI state matches submit behavior.
Suggested adjustment
+const trimmedTitle = title.trim();
+const parsedAmount = Number(amount.trim());
+const isAmountValid = Number.isFinite(parsedAmount) && parsedAmount > 0;
+
<button
className={primaryButtonClass()}
- disabled={create.isPending || !title || !amount}
+ disabled={create.isPending || !trimmedTitle || !isAmountValid}
type="button"
onClick={submit}
>Also applies to: 321-325
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/src/components/explore/jobs/Jobs.tsx` around lines 206 - 223, The
button's disabled state logic does not match the validation rules applied in the
submit handler, causing the button to appear enabled for invalid inputs like
whitespace-only titles or non-positive amounts until the user clicks and the
submit validation fails. Update the disabled condition on the submit button to
use the same normalized validation checks as the submit function: validate the
trimmed title is not empty, validate the parsed amount is finite and greater
than 0, and check that agentId exists. This ensures the button is only enabled
when all validation conditions in the submit handler would pass.
Domain registration built the payer-signed x402 TransferChecked against the SDK's hardcoded mainnet USDC mint while the app runs on devnet, so the on-chain payment reverted (the mainnet mint account and its derived ATAs don't exist on devnet). The staging backend advertises the devnet USDC mint (4zMMC9...DncDU) via GET /payments/supported. Set NEXT_PUBLIC_SOLANA_USDC_MINT to that mint and make publicUsdcMint() fall back to the devnet mint by active cluster when unset, so a devnet build never silently settles against the mainnet mint. Dedupe use-wallet-balances onto the same resolver.
The language toggle changed only a handful of feed/home/poker strings because the high-traffic chrome — primary nav, sidebar links, the Need-an-Agent CTA and the Settings page itself — rendered hardcoded English and never went through t(). Switching to Spanish appeared to do nothing. Add nav + settings namespaces to the en/es resources and route those surfaces through t() (section labels, Docs/Feedback/Settings links, menu aria-labels, the Settings title/description/language/theme headings and theme names). Brand proper nouns (Discord/X/GitHub) stay literal.
The peer-resolution test still asserted the old wallet-key fallback that the DM fix (abc0bc2) deliberately removed. Update it to assert resolveDirectoryPeer now surfaces an actionable error when the card advertises no encryption key, instead of returning the wallet key that would 404 at keys.getBundle.
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
website/src/components/explore/Settings.tsx (1)
118-123:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd JSDoc for exported
Settingscomponent.Line 118 exports a public component without JSDoc.
✍️ Proposed fix
+/** + * Renders language and theme preferences for the current user interface. + */ export const Settings = ({ isDark }: SettingsProperties): FunctionComponent => { const flavor = useAppStore((state) => state.flavor); const setFlavor = useAppStore((state) => state.setFlavor);As per coding guidelines, "Always include JSDoc comments for public functions and exported modules."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/components/explore/Settings.tsx` around lines 118 - 123, The exported Settings component is missing JSDoc documentation. Add a JSDoc comment block above the Settings component export that documents the component's purpose, its props (including the isDark property from SettingsProperties), and its return type as FunctionComponent. The JSDoc should provide a clear description of what the Settings component does and how it's used.Source: Coding guidelines
website/src/components/layout/Sidebar.tsx (1)
207-213:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd JSDoc for exported
Sidebarcomponent.Line 207 exports a public component without JSDoc.
✍️ Proposed fix
+/** + * Renders the primary application sidebar for desktop and mobile navigation. + */ export const Sidebar = ({ activeSection, isDark, sections, }: SidebarProps): FunctionComponent => {As per coding guidelines, "Always include JSDoc comments for public functions and exported modules."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@website/src/components/layout/Sidebar.tsx` around lines 207 - 213, The exported Sidebar component at line 207 is missing JSDoc documentation. Add a JSDoc comment block above the Sidebar component export that documents the component's purpose, its props (activeSection, isDark, sections), the SidebarProps type that defines these parameters, and the return type (FunctionComponent). Ensure the JSDoc follows standard format with description, `@param` entries for each prop, and `@returns` to document the return type.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/.env`:
- Line 8: The NEXT_PUBLIC_SOLANA_USDC_MINT environment variable in the .env file
currently has its value wrapped in double quotes, which triggers a
QuoteCharacter warning in dotenv-linter. Remove the double quotes around the
mint value "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU" so that the variable
assignment becomes
NEXT_PUBLIC_SOLANA_USDC_MINT=4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU
without quotes, which will resolve the linter warning.
---
Outside diff comments:
In `@website/src/components/explore/Settings.tsx`:
- Around line 118-123: The exported Settings component is missing JSDoc
documentation. Add a JSDoc comment block above the Settings component export
that documents the component's purpose, its props (including the isDark property
from SettingsProperties), and its return type as FunctionComponent. The JSDoc
should provide a clear description of what the Settings component does and how
it's used.
In `@website/src/components/layout/Sidebar.tsx`:
- Around line 207-213: The exported Sidebar component at line 207 is missing
JSDoc documentation. Add a JSDoc comment block above the Sidebar component
export that documents the component's purpose, its props (activeSection, isDark,
sections), the SidebarProps type that defines these parameters, and the return
type (FunctionComponent). Ensure the JSDoc follows standard format with
description, `@param` entries for each prop, and `@returns` to document the return
type.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 72ad56f3-1322-4936-ad04-720e4229109f
📒 Files selected for processing (8)
website/.envwebsite/src/assets/locales/en/translations.jsonwebsite/src/assets/locales/es/translations.jsonwebsite/src/common/delegated-payment.tswebsite/src/common/solana-rpc.tswebsite/src/components/explore/Settings.tsxwebsite/src/components/layout/Sidebar.tsxwebsite/src/hooks/use-wallet-balances.ts
✅ Files skipped from review due to trivial changes (2)
- website/src/common/solana-rpc.ts
- website/src/assets/locales/en/translations.json
Mark-read/archive/delete fired with owner undefined during the owned-identities lookup window, acting as the agent-auth actor before a real @handle resolved. Gate the buttons on the same isInboxActorResolving flag the query already uses so they wait for the actor to settle.
Button enabled for whitespace titles and non-positive budgets, then failed only on click. Derive canSubmit from the same trimmed-title + positive-amount checks submit() runs.
Clears the dotenv-linter QuoteCharacter warning; the mint is a plain base58 string needing no quoting.
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
resolveEncryptionAddress threw whenever a card lacked the website-only encryptionPublicKey metadata. SDK/CLI agents publish their Signal bundle under their wallet key (signer.publicKeyBase64 === card.publicKey) and never set that metadata, so the throw made website->CLI DMs and group key handoff to those members fail even though a bundle existed. Restore the publicKey fallback so those peers stay addressable, and move the actionable 'hasn't enabled encrypted messaging' signal to send time: encryptEnvelope now maps the bootstrap keys.getBundle 404 to that error, so agents that genuinely never enabled encryption still fail loudly instead of leaking a raw 404. Throw at resolve only when a card has neither an advertised key nor a publicKey.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@sdk/typescript/src/messaging/encryption.ts`:
- Around line 111-128: Add test coverage for the new `fetchBundleForBootstrap`
method in encryption.ts to verify its error translation logic. In
encrypted-messaging.test.ts, create two test cases: the first should verify that
when `keys.getBundle` receives a 404 error from a recipient without a published
bundle, it throws the user-friendly error message "Recipient hasn't enabled
encrypted messaging yet", and the second should verify that non-404 errors from
`keys.getBundle` are rethrown unchanged without translation. Leverage the
existing mock relay infrastructure that already supports returning 404 responses
(as shown at lines 67-68) to simulate the missing bundle scenario in the first
test case.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 1a4664e7-afc3-4bff-8a70-25048d50e30d
📒 Files selected for processing (7)
sdk/typescript/src/messaging/discovery.tssdk/typescript/src/messaging/encryption.tssdk/typescript/tests/encryption-discovery.test.tswebsite/.envwebsite/src/common/peer-resolution.test.tswebsite/src/components/explore/Inbox.tsxwebsite/src/components/explore/jobs/Jobs.tsx
🚧 Files skipped from review as they are similar to previous changes (4)
- sdk/typescript/src/messaging/discovery.ts
- website/.env
- website/src/components/explore/Inbox.tsx
- website/src/components/explore/jobs/Jobs.tsx
| /** | ||
| * Fetch a recipient's key bundle to bootstrap the first X3DH session. A 404 | ||
| * here means the address has no published Signal bundle — the agent never | ||
| * enabled encrypted messaging (their directory card may advertise a key, or we | ||
| * may have optimistically fallen back to their wallet publicKey, but no bundle | ||
| * was ever uploaded). Translate it into an actionable error instead of leaking | ||
| * the raw 404 to the send call site. | ||
| */ | ||
| private async fetchBundleForBootstrap(to: string): Promise<KeyBundle> { | ||
| try { | ||
| return await this.keys.getBundle(to); | ||
| } catch (error) { | ||
| if (error instanceof TinyPlaceError && error.status === 404) { | ||
| throw new Error("Recipient hasn't enabled encrypted messaging yet"); | ||
| } | ||
| throw error; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Candidate encryption-related tests:"
fd -i 'encryption*test*.ts' sdk/typescript/tests
echo
echo "Search for assertions around missing-bundle messaging and bundle fetch failures:"
rg -n -C3 "hasn't enabled encrypted messaging|getBundle\\(|TinyPlaceError|status\\s*===\\s*404|encryptEnvelope\\(" sdk/typescript/testsRepository: tinyhumansai/tiny.place
Length of output: 15487
🏁 Script executed:
# Check if there are any test files that import from encryption.ts
rg -l "from.*encryption|import.*encryption" sdk/typescript/tests --type ts
# Look at the encryption-discovery test to see its scope
cat -n sdk/typescript/tests/encryption-discovery.test.ts
# Search for any mocks or tests of fetchBundleForBootstrap or encryptEnvelope with error handling
rg -n "fetchBundleForBootstrap|mock.*getBundle|404|encryptEnvelope" sdk/typescript/tests --type ts -C2Repository: tinyhumansai/tiny.place
Length of output: 7458
🏁 Script executed:
cat -n sdk/typescript/tests/encrypted-messaging.test.tsRepository: tinyhumansai/tiny.place
Length of output: 7244
Add tests for the new bundle-fetch error translation in fetchBundleForBootstrap.
The new fetchBundleForBootstrap helper at lines 111–128 translates 404 errors from keys.getBundle into a user-friendly message. This error path is not covered by existing tests. While encrypted-messaging.test.ts provides mock relay infrastructure capable of returning 404 for missing bundles (line 67–68), no test case exercises sending a message to a recipient without a published bundle, which is the exact scenario fetchBundleForBootstrap handles.
Add at least two test cases:
- Sending to a recipient without a published bundle returns
"Recipient hasn't enabled encrypted messaging yet" - Non-404 errors from
keys.getBundleare rethrown unchanged
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@sdk/typescript/src/messaging/encryption.ts` around lines 111 - 128, Add test
coverage for the new `fetchBundleForBootstrap` method in encryption.ts to verify
its error translation logic. In encrypted-messaging.test.ts, create two test
cases: the first should verify that when `keys.getBundle` receives a 404 error
from a recipient without a published bundle, it throws the user-friendly error
message "Recipient hasn't enabled encrypted messaging yet", and the second
should verify that non-404 errors from `keys.getBundle` are rethrown unchanged
without translation. Leverage the existing mock relay infrastructure that
already supports returning 404 responses (as shown at lines 67-68) to simulate
the missing bundle scenario in the first test case.
Source: Coding guidelines
Summary
Second batch of staging.tiny.place bug fixes — 8 issues, one micro-commit each. Independent of #123 (no file overlap).
Fixes
Messaging
directory.getAgent. Now classifies input unambiguously (handle → base64 raw key → base58 cryptoId), andresolveEncryptionAddressno longer silently falls back to the wallet identity key (which has no Signal key bundle → send 404s later) — it throws an actionable error at resolve time. (SDK + website)@handlehad resolved, so the backend rejected the directory-as request (401/403/404). Now uses the agent-auth path when there's no handle, holds the query until owned-identities settles, and treats 401/403/404 alike as the actionable connect-a-handle state.Payments
TransferCheckedfor registration was built against the SDK's hardcoded mainnet USDC mint while the app runs on devnet, so the on-chain payment reverted (the mint account + its derived ATAs don't exist on the wrong cluster). The staging backend advertises the devnet USDC mint (4zMMC9…DncDU) viaGET /payments/supported. SetNEXT_PUBLIC_SOLANA_USDC_MINTto it and makepublicUsdcMint()fall back to the devnet mint by active cluster when unset, so a devnet build never silently settles against the mainnet mint; dedupeduse-wallet-balancesonto the same resolver.Activity
describe()switch didn't list, notably the backend'sledger.<TYPE>fallback kinds and canonicalgroup.fee/arbitration.fee/fee/event.refund. Extracted one shared formatter (used by the Activity list + header marquee) that normalizesledger.<TYPE>to its canonical kind, adds the missing cases, and humanizes unknown kinds.Marketplace
i18n
t(). Addednav+settingsnamespaces to the en/es resources and routed those surfaces throught()(section labels, Docs/Feedback/Settings links, menu aria-labels, Settings headings + theme names). Brand proper nouns (Discord/X/GitHub) stay literal. Remaining deep-view strings are a tracked follow-up.UI / perf
x.com/intent/followpopup; now the canonicalhttps://x.com/tinyhumansaiprofile.Testing
pnpm --filter @tinyplace/website lint/build— greenactivity-describe.test.ts(ledger.* normalization), SDKencryption-discovery.test.ts(resolveEncryptionAddress throw)discovery.tschange) before website typecheckGET https://staging-api.tiny.place/payments/supported(Solana USDC mint =4zMMC9…DncDU)SubtleCryptoEd25519 test failures (auth-payment/session-wallet/OnboardWizard) are a local-Node env issue, unrelated to this diff; CI on Node 22 passes.Note
Contains an
sdk/typescript/change (discovery.ts) — merging tomainwill trigger the TS SDK publish workflow.Follow-up
t()extraction — tracked in i18n: extend translation coverage beyond chrome (deep explore views) #138.Summary by CodeRabbit
Bug Fixes
New Features
Improvements