* Extract an account-scoping string for the idempotency cache from the * tool arguments. Mirrors `sessionKeyFromArgs` but returns just the scope * portion (no `open:` prefix) so callers can feed it to `scopedPrincipal`. * * The scope is caller-controlled, so it doesn't authenticate anything — *
(args: Record<string, unknown>)
| 293 | * from seeing each other's idempotency outcomes. |
| 294 | */ |
| 295 | function deriveAccountScope(args: Record<string, unknown>): string | undefined { |
| 296 | const account = (args.account as { account_id?: string; brand?: { domain?: string } } | undefined); |
| 297 | if (account?.account_id && typeof account.account_id === 'string') { |
| 298 | return `a:${account.account_id}`; |
| 299 | } |
| 300 | const domain = account?.brand?.domain |
| 301 | ?? (args.brand as { domain?: string } | undefined)?.domain; |
| 302 | if (typeof domain === 'string' && domain.length > 0) { |
| 303 | return `b:${domain.toLowerCase()}`; |
| 304 | } |
| 305 | return undefined; |
| 306 | } |
| 307 | |
| 308 | /** Clear the task store (for tests). Calls cleanup() to cancel TTL timers. */ |
| 309 | export function clearTaskStore(): void { |