From 62387c739e29b7e8b56edf128d01c27ca224ae44 Mon Sep 17 00:00:00 2001 From: Benjamin Shafii Date: Mon, 6 Jan 2025 16:57:13 +0100 Subject: [PATCH] feat: add completion step --- .../src/agents/payment-detector-agent.ts | 65 +- .../src/agents/payment-preparer-agent.ts | 45 +- pipes/auto-pay/src/pages/index.tsx | 1019 ++++++++++------- .../auto-pay/src/stores/agent-steps-store.ts | 117 +- pipes/auto-pay/src/types/wise.ts | 1 + 5 files changed, 730 insertions(+), 517 deletions(-) diff --git a/pipes/auto-pay/src/agents/payment-detector-agent.ts b/pipes/auto-pay/src/agents/payment-detector-agent.ts index 63d4c15..cb3420e 100644 --- a/pipes/auto-pay/src/agents/payment-detector-agent.ts +++ b/pipes/auto-pay/src/agents/payment-detector-agent.ts @@ -5,18 +5,19 @@ import { useAgentStepsStore } from '@/stores/agent-steps-store'; import { toast } from '@/components/ui/use-toast'; import { useCallback, useState, useRef, useEffect } from 'react'; import { z } from 'zod'; +import type { PaymentInfo } from '@/types/wise'; // Zod schemas const bankDetailsSchema = z.object({ - accountNumber: z.string().optional().nullable(), - routingNumber: z.string().optional().nullable(), - iban: z.string().optional().nullable(), + accountNumber: z.string().optional(), + routingNumber: z.string().optional(), + iban: z.string().optional(), }).describe('Bank account details for the payment'); const paymentDetailsSchema = z.object({ - amount: z.string().optional(), - currency: z.string().optional(), - recipient: z.string().optional().nullable(), + amount: z.string(), + currency: z.string(), + recipient: z.string(), dueDate: z.string().optional(), bankDetails: bankDetailsSchema.optional(), reference: z.string().optional(), @@ -51,6 +52,7 @@ export interface DetectedPayment { window: string; }; details: PaymentDetails; + paymentInfo: PaymentInfo; } export interface PaymentDetectionResult { @@ -87,6 +89,17 @@ const paymentAnswer = { parameters: paymentAnswerSchema, }; +function paymentDetailsToPaymentInfo(details: PaymentDetails): PaymentInfo { + return { + amount: details.amount || '0', + currency: details.currency || 'USD', + recipientName: details.recipient || 'Unknown Recipient', + accountNumber: details.bankDetails?.accountNumber || '', + routingNumber: details.bankDetails?.routingNumber || '', + reference: details.reference, + }; +} + export async function runPaymentDetector( recognizedItemId: string, onProgress?: (message: string) => void, @@ -124,23 +137,33 @@ export async function runPaymentDetector( - Payment amounts and currencies - Payment deadlines or due dates - Stop as soon as you found 1 - + Stop as soon as you found 1 payment. Follow these steps: 1. Start with broad searches for payment-related terms: - make sure queries are single elements that are will be matched as if they were between double quotes - Use terms like "invoice", "payment", "transfer", "IBAN", "due", "amount" + - Make sure queries are single elements that will be matched exactly 2. When you find something, do focused searches to gather context: - - General informatino that can be used to identify the payment - - 4. Once you have gathered all information: + - Look for specific amounts and currencies + - Search for recipient information + - Find any payment references or notes + - Check for due dates or deadlines + + 3. For each potential payment: + - Extract a clear summary + - Note the source context + - Calculate confidence based on completeness + - Explain your confidence reasoning + + 4. Once you have gathered all information: - Call paymentAnswer with the structured payment data - Include all found details (amount, recipient, due date, etc.) - Provide clear summaries and confidence scores - Explain your confidence reasoning + BE THOROUGH BUT EFFICIENT + FOCUS ON ACCURACY OVER SPEED `, prompt: ` Search through recent screen activity to find any payments that need to be made. @@ -162,7 +185,7 @@ export async function runPaymentDetector( const stepId = crypto.randomUUID(); const humanAction = getHumanActionFromToolCall(toolCall); - // Add the step with the action + // Add the step with all information addStep(recognizedItemId, { text, toolCalls: [toolCall], @@ -170,6 +193,7 @@ export async function runPaymentDetector( finishReason, usage, humanAction, + tokenCount: usage?.totalTokens || 0, }); // If we have results, update with human result @@ -177,13 +201,13 @@ export async function runPaymentDetector( const humanResult = getHumanResultFromToolCall(toolCall, toolResults[index]); updateStepResult(recognizedItemId, stepId, humanResult); } - }); - // Notify progress - if (toolCalls?.length && onProgress) { - const toolNames = toolCalls.map(t => 'toolName' in t ? t.toolName : 'unknown').join(', '); - onProgress(`Using tools: ${toolNames}`); - } + // Notify progress + if (onProgress) { + const toolName = 'toolName' in toolCall ? toolCall.toolName : 'unknown'; + onProgress(`Using tool: ${toolName}`); + } + }); }, }); @@ -208,7 +232,8 @@ export async function runPaymentDetector( app: '', window: '', }, - details: payment.details + details: payment.details, + paymentInfo: paymentDetailsToPaymentInfo(payment.details) })); // Sort by confidence (most confident first) diff --git a/pipes/auto-pay/src/agents/payment-preparer-agent.ts b/pipes/auto-pay/src/agents/payment-preparer-agent.ts index a8b8eef..cfecf4a 100644 --- a/pipes/auto-pay/src/agents/payment-preparer-agent.ts +++ b/pipes/auto-pay/src/agents/payment-preparer-agent.ts @@ -5,6 +5,7 @@ import { useAgentStepsStore } from '@/stores/agent-steps-store'; import { toast } from '@/components/ui/use-toast'; import { useCallback, useState, useRef, useEffect } from 'react'; import { z } from 'zod'; +import type { PaymentInfo } from '@/types/wise'; // Zod schemas for Wise transfer data const transferDetailsSchema = z.object({ @@ -125,6 +126,18 @@ export async function runPaymentPreparer( - Validate bank details where possible - Include clear references + 4. Calculate confidence based on: + - Completeness of required fields + - Clarity of the information + - Validation of bank details + - Consistency across sources + + 5. Provide detailed explanation of: + - Why certain details were chosen + - What might be missing + - Any assumptions made + - Validation results + BE THOROUGH BUT EFFICIENT FOCUS ON ACCURACY OVER SPEED `, @@ -148,7 +161,7 @@ export async function runPaymentPreparer( const stepId = crypto.randomUUID(); const humanAction = getHumanActionFromToolCall(toolCall); - // Add the step with the action + // Add the step with all information addStep(recognizedItemId, { text, toolCalls: [toolCall], @@ -156,6 +169,7 @@ export async function runPaymentPreparer( finishReason, usage, humanAction, + tokenCount: usage?.totalTokens || 0, }); // If we have results, update with human result @@ -163,13 +177,13 @@ export async function runPaymentPreparer( const humanResult = getHumanResultFromToolCall(toolCall, toolResults[index]); updateStepResult(recognizedItemId, stepId, humanResult); } - }); - // Notify progress - if (toolCalls?.length && onProgress) { - const toolNames = toolCalls.map(t => 'toolName' in t ? t.toolName : 'unknown').join(', '); - onProgress(`Using tools: ${toolNames}`); - } + // Notify progress + if (onProgress) { + const toolName = 'toolName' in toolCall ? toolCall.toolName : 'unknown'; + onProgress(`Using tool: ${toolName}`); + } + }); }, }); @@ -317,4 +331,21 @@ export function usePaymentPreparer(recognizedItemId: string) { isProcessing, abort, }; +} + +function transferDetailsToPaymentInfo(details: TransferDetails): PaymentInfo { + // Ensure we have non-null values for required fields + if (!details.amount || !details.currency || !details.targetAccount.accountHolderName) { + throw new Error('Missing required transfer details'); + } + + return { + amount: details.amount, + currency: details.currency, + recipientName: details.targetAccount.accountHolderName, + accountNumber: details.targetAccount.accountNumber || '', + routingNumber: details.targetAccount.routingNumber || '', + reference: details.reference || undefined, + recipientEmail: undefined, // We don't have this in transfer details + }; } \ No newline at end of file diff --git a/pipes/auto-pay/src/pages/index.tsx b/pipes/auto-pay/src/pages/index.tsx index 88226b9..1d41247 100644 --- a/pipes/auto-pay/src/pages/index.tsx +++ b/pipes/auto-pay/src/pages/index.tsx @@ -1,17 +1,36 @@ -import * as React from "react"; +import * as React from 'react'; import { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from '@/components/ui/card'; import { toast } from '@/components/ui/use-toast'; -import { ReloadIcon, ArrowRightIcon, CheckCircledIcon, CrossCircledIcon, MagnifyingGlassIcon } from "@radix-ui/react-icons"; -import { Progress } from "@/components/ui/progress"; -import { Badge } from "@/components/ui/badge"; -import { Separator } from "@/components/ui/separator"; -import { ScrollArea } from "@/components/ui/scroll-area"; +import { + ReloadIcon, + ArrowRightIcon, + CheckCircledIcon, + CrossCircledIcon, + MagnifyingGlassIcon, +} from '@radix-ui/react-icons'; +import { Progress } from '@/components/ui/progress'; +import { Badge } from '@/components/ui/badge'; +import { Separator } from '@/components/ui/separator'; +import { ScrollArea } from '@/components/ui/scroll-area'; import { AgentStepsView } from '@/components/agent-steps-view'; import type { PaymentInfo } from '@/types/wise'; -import { usePaymentDetector, type DetectedPayment } from '@/agents/payment-detector-agent'; -import { usePaymentPreparer, type TransferDetails } from '@/agents/payment-preparer-agent'; +import { + usePaymentDetector, + type DetectedPayment, +} from '@/agents/payment-detector-agent'; +import { + usePaymentPreparer, + type TransferDetails, +} from '@/agents/payment-preparer-agent'; import { useAgentStepsStore } from '@/stores/agent-steps-store'; // Convert TransferDetails to PaymentInfo @@ -19,462 +38,600 @@ function transferDetailsToPaymentInfo(details: TransferDetails): PaymentInfo { return { amount: details.amount, currency: details.currency, - recipientName: details.targetAccount.accountHolderName, + recipientName: details.targetAccount.accountHolderName || '', accountNumber: details.targetAccount.accountNumber || '', routingNumber: details.targetAccount.routingNumber || '', - reference: details.reference, + reference: details.reference || '', }; } +interface WiseTransferDetails { + id: string; + status: string; + wiseUrl: string; +} + export default function Home() { - const [step, setStep] = useState<'idle' | 'detecting' | 'detected' | 'preparing' | 'review' | 'creating' | 'funding'>('idle'); - const [selectedPayment, setSelectedPayment] = useState(null); - const [paymentInfo, setPaymentInfo] = useState(null); - const [transferId, setTransferId] = useState(null); - const [creatingTransfer, setCreatingTransfer] = useState(false); - const [fundingTransfer, setFundingTransfer] = useState(false); - const [recognizedItemId] = useState(() => crypto.randomUUID()); + const [step, setStep] = useState< + | 'idle' + | 'detecting' + | 'detected' + | 'preparing' + | 'review' + | 'creating' + | 'funding' + >('idle'); + const [selectedPayment, setSelectedPayment] = + useState(null); + const [paymentInfo, setPaymentInfo] = useState(null); + const [transferDetails, setTransferDetails] = + useState(null); + const [creatingTransfer, setCreatingTransfer] = useState(false); + const [fundingTransfer, setFundingTransfer] = useState(false); + const [recognizedItemId] = useState(() => crypto.randomUUID()); - // Use both agents with abort capability - const { result: detectionResult, detectPayments, isProcessing: isDetecting, abort: abortDetection } = usePaymentDetector(recognizedItemId); - const { result: preparationResult, prepareTransfer, isProcessing: isPreparing, abort: abortPreparation } = usePaymentPreparer(recognizedItemId); + // Remove separate transferId state and use transferDetails.id instead + const transferId = transferDetails?.id; - // Clear steps when component unmounts - useEffect(() => { - return () => { - useAgentStepsStore.getState().clearSteps(recognizedItemId); - }; - }, [recognizedItemId]); + // Use both agents with abort capability + const { + result: detectionResult, + detectPayments, + isProcessing: isDetecting, + abort: abortDetection, + } = usePaymentDetector(recognizedItemId); + const { + result: preparationResult, + prepareTransfer, + isProcessing: isPreparing, + abort: abortPreparation, + } = usePaymentPreparer(recognizedItemId); - const handleDetect = async () => { - setStep('detecting'); - const result = await detectPayments(); - if (result.payments.length > 0) { - setStep('detected'); - } else { - setStep('idle'); - } + // Clear steps when component unmounts + useEffect(() => { + return () => { + useAgentStepsStore.getState().clearSteps(recognizedItemId); }; + }, [recognizedItemId]); - const handlePreparePayment = useCallback(async (payment: DetectedPayment) => { - setSelectedPayment(payment); - setStep('preparing'); - - const result = await prepareTransfer(payment.vitalInfo); - - if ('error' in result) { - toast({ - title: 'Preparation Failed', - description: result.error, - variant: 'destructive' - }); - setStep('idle'); - return; - } - - if (result.transfer) { - setPaymentInfo(transferDetailsToPaymentInfo(result.transfer.details)); - setStep('review'); - } else { - setStep('idle'); - } - }, [prepareTransfer, toast]); + const handleDetect = async () => { + setStep('detecting'); + const result = await detectPayments(); + if (result.payments.length > 0) { + setStep('detected'); + } else { + setStep('idle'); + } + }; - const handleCreateTransfer = async () => { - if (!paymentInfo) return; - try { - setCreatingTransfer(true); - setStep('creating'); - const res = await fetch('/api/createTransfer', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ paymentInfo }) - }); - const data = await res.json(); - if (data.id) { - setTransferId(data.id); - setStep('funding'); - toast({ - title: 'Transfer Created', - description: `Transfer #${data.id} has been created successfully.` - }); - } - } catch (error) { - console.error('Failed to create transfer:', error); - toast({ - title: 'Error', - description: 'Failed to create transfer', - variant: 'destructive' - }); - setStep('review'); - } finally { - setCreatingTransfer(false); - } - }; + const handlePreparePayment = useCallback( + async (payment: DetectedPayment) => { + setSelectedPayment(payment); + setStep('preparing'); + + const result = await prepareTransfer(payment.vitalInfo); + + if ('error' in result) { + toast({ + title: 'Preparation Failed', + description: result.error, + variant: 'destructive', + }); + setStep('idle'); + return; + } - const handleFundTransfer = async () => { - if (!transferId) return; + if (result.transfer) { try { - setFundingTransfer(true); - await fetch('/api/fundTransfer', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ transferId }) - }); - toast({ - title: 'Success!', - description: 'Transfer has been funded and is being processed.', - }); - // Reset the flow - setTimeout(() => { - setStep('idle'); - setSelectedPayment(null); - setPaymentInfo(null); - setTransferId(null); - useAgentStepsStore.getState().clearSteps(recognizedItemId); - }, 3000); + // Try to convert transfer details to payment info + const paymentInfo = transferDetailsToPaymentInfo( + result.transfer.details + ); + setPaymentInfo(paymentInfo); + setStep('review'); } catch (error) { - console.error('Failed to fund transfer:', error); - toast({ - title: 'Error', - description: 'Failed to fund transfer', - variant: 'destructive' - }); - } finally { - setFundingTransfer(false); + // If conversion fails, use the original payment info from detection + console.log('0xHypr', 'Using detected payment info as fallback'); + setPaymentInfo(payment.paymentInfo); + setStep('review'); } - }; + } else { + setStep('idle'); + } + }, + [prepareTransfer, toast] + ); - const getStepProgress = () => { - switch (step) { - case 'idle': return 0; - case 'detecting': return 15; - case 'detected': return 30; - case 'preparing': return 45; - case 'review': return 60; - case 'creating': return 75; - case 'funding': return 90; - default: return 0; - } - }; + const handleCreateTransfer = async () => { + if (!paymentInfo) return; + try { + setCreatingTransfer(true); + setStep('creating'); + const res = await fetch('/api/createTransfer', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ paymentInfo }), + }); + const data = await res.json(); + if (data.transfer?.id) { + const transferId = data.transfer.id.toString(); + const status = + typeof data.transfer.status === 'string' + ? data.transfer.status + : 'created'; + // sample link https://sandbox.transferwise.tech/transactions/activities/by-resource/TRANSFER/54689164 + const baseUrl = process.env.NEXT_PUBLIC_WISE_SANDBOX + ? 'https://sandbox.transferwise.tech' + : 'https://wise.com'; + + const wiseUrl = `${baseUrl}/transactions/activities/by-resource/TRANSFER/${transferId}`; + setTransferDetails({ + id: transferId, + status, + wiseUrl, + }); + setStep('funding'); + toast({ + title: 'Transfer Created', + description: `Transfer #${transferId} has been created successfully.`, + }); + } + } catch (error) { + console.error('Failed to create transfer:', error); + toast({ + title: 'Error', + description: 'Failed to create transfer', + variant: 'destructive', + }); + setStep('review'); + } finally { + setCreatingTransfer(false); + } + }; + + const handleFundTransfer = async () => { + if (!transferDetails) return; + try { + setFundingTransfer(true); + await fetch('/api/fundTransfer', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ transferId: transferDetails.id }), + }); + toast({ + title: 'Success!', + description: 'Transfer has been funded and is being processed.', + }); + // Reset the flow + setTimeout(() => { + setStep('idle'); + setSelectedPayment(null); + setPaymentInfo(null); + setTransferDetails(null); + useAgentStepsStore.getState().clearSteps(recognizedItemId); + }, 3000); + } catch (error) { + console.error('Failed to fund transfer:', error); + toast({ + title: 'Error', + description: 'Failed to fund transfer', + variant: 'destructive', + }); + } finally { + setFundingTransfer(false); + } + }; + + const getStepProgress = () => { + switch (step) { + case 'idle': + return 0; + case 'detecting': + return 15; + case 'detected': + return 30; + case 'preparing': + return 45; + case 'review': + return 60; + case 'creating': + return 75; + case 'funding': + return 90; + default: + return 0; + } + }; + + return ( +
+
+
+ {/* Header */} +
+

Auto-Pay

+

+ Automatically process payments from your screen activity +

+
+ + {/* Progress */} +
+ +
+ Detect + Prepare + Review + Create + Fund +
+
- return ( -
-
-
- {/* Header */} -
-

Auto-Pay

-

- Automatically process payments from your screen activity +

+ {/* Main Content */} +
+ {step === 'idle' && ( + + + Start New Payment + + We'll analyze your recent screen activity to detect + payment information + + + + + + + )} + + {step === 'detecting' && ( + + +
+ +
+

Detecting Payments

+

+ Looking for payment information...

+
+
+
+
+ )} - {/* Progress */} -
- -
- Detect - Prepare - Review - Create - Fund + {step === 'detected' && detectionResult?.payments && ( + + + Detected Payments + + Select a payment to prepare + + + +
+ {detectionResult.payments.map((payment) => ( +
handlePreparePayment(payment)} + > +
+
{payment.summary}
+
+ {payment.source.app} -{' '} + {new Date(payment.timestamp).toLocaleString()} +
+
+ + {payment.confidence}% confidence +
+ ))}
+
+ + + +
+ )} -
- {/* Main Content */} -
- {step === 'idle' && ( - - - Start New Payment - - We'll analyze your recent screen activity to detect payment information - - - - - - - )} - - {step === 'detecting' && ( - - -
- -
-

Detecting Payments

-

- Looking for payment information... -

-
- -
-
-
- )} + {step === 'preparing' && ( + + +
+ +
+

Preparing Payment

+

+ Extracting payment details... +

+
+ +
+
+
+ )} - {step === 'detected' && detectionResult?.payments && ( - - - Detected Payments - - Select a payment to prepare - - - -
- {detectionResult.payments.map((payment) => ( -
handlePreparePayment(payment)} - > -
-
{payment.summary}
-
- {payment.source.app} - {new Date(payment.timestamp).toLocaleString()} -
-
- - {payment.confidence}% confidence - -
- ))} -
-
- - - -
- )} + {step === 'review' && paymentInfo && ( + + +
+ Review Payment Details + + {paymentInfo.currency} + +
+ + Please verify the payment information + +
+ + {/* Payment Amount */} +
+
+ {new Intl.NumberFormat('en-US', { + style: 'currency', + currency: paymentInfo.currency, + }).format(Number(paymentInfo.amount))} +
+
+ Total Amount +
+
- {step === 'preparing' && ( - - -
- -
-

Preparing Payment

-

- Extracting payment details... -

-
- -
-
-
- )} + {/* Bank Details */} +
+

Bank Account Details

+
+
+ +
+ {paymentInfo.accountNumber} +
+
+
+ +
+ {paymentInfo.routingNumber} +
+
+
+
- {step === 'review' && paymentInfo && ( - - -
- Review Payment Details - - {paymentInfo.currency} - -
- - Please verify the payment information - -
- - {/* Payment Amount */} -
-
- {new Intl.NumberFormat('en-US', { - style: 'currency', - currency: paymentInfo.currency - }).format(Number(paymentInfo.amount))} -
-
- Total Amount -
-
+ - {/* Bank Details */} -
-

Bank Account Details

-
-
- -
- {paymentInfo.accountNumber} -
-
-
- -
- {paymentInfo.routingNumber} -
-
-
-
+ {/* Additional Details */} + {paymentInfo.recipientName && ( +
+ +
+ {paymentInfo.recipientName} +
+
+ )} + {paymentInfo.reference && ( +
+ +
+ {paymentInfo.reference} +
+
+ )} +
+ + + + +
+ )} - + {(step === 'creating' || step === 'funding') && + transferDetails && ( + + + + {step === 'creating' + ? 'Creating Transfer' + : 'Processing Payment'} + + + Transfer #{transferDetails.id} + + + +
+ {/* Transfer Status */} +
+
+
+
+ Status +
+
+ {transferDetails.status || 'Processing'} +
+
+ {step === 'funding' && ( + Ready to Fund + )} +
+
- {/* Additional Details */} - {paymentInfo.recipientName && ( -
- -
- {paymentInfo.recipientName} -
-
- )} - {paymentInfo.reference && ( -
- -
- {paymentInfo.reference} -
-
- )} - - - - - - + {/* Payment Details */} + {paymentInfo && ( +
+
+
+ Amount +
+
+ {new Intl.NumberFormat('en-US', { + style: 'currency', + currency: paymentInfo.currency, + }).format(Number(paymentInfo.amount))} +
+
+
+
+ Recipient +
+
+ {paymentInfo.recipientName} +
+
+ {paymentInfo.reference && ( +
+
+ Reference +
+
+ {paymentInfo.reference} +
+
)} +
+ )} - {(step === 'creating' || step === 'funding') && transferId && ( - - - - {step === 'creating' ? 'Creating Transfer' : 'Processing Payment'} - - - Transfer #{transferId} - - - -
-
- -
- {step === 'funding' && ( - - )} -
-
-
+ {/* Wise Link */} + {transferDetails.wiseUrl && ( + + )} + + {/* Fund Button */} + {step === 'funding' && ( +
+ + )} +
+ + + )} +
- {/* Agent Steps Sidebar */} -
- - - Agent Progress - - {step === 'detecting' || step === 'detected' - ? 'Payment detection steps' - : step === 'preparing' || step === 'review' - ? 'Payment preparation steps' - : 'Real-time progress'} - - - - - - -
-
-
+ {/* Agent Steps Sidebar */} +
+ + + Agent Progress + + {step === 'detecting' || step === 'detected' + ? 'Payment detection steps' + : step === 'preparing' || step === 'review' + ? 'Payment preparation steps' + : 'Real-time progress'} + + + + + +
+
- ); +
+
+ ); } diff --git a/pipes/auto-pay/src/stores/agent-steps-store.ts b/pipes/auto-pay/src/stores/agent-steps-store.ts index 9d784cb..366460c 100644 --- a/pipes/auto-pay/src/stores/agent-steps-store.ts +++ b/pipes/auto-pay/src/stores/agent-steps-store.ts @@ -1,82 +1,81 @@ import { create } from 'zustand'; -import { CoreToolCallUnion } from 'ai'; export interface AgentStep { id: string; - timestamp: number; - humanAction?: string; - humanResult?: string; - text?: string; - toolCalls?: CoreToolCallUnion[]; - toolResults?: unknown[]; + timestamp: string; + text: string; + tokenCount: number; + toolCalls: any[]; + toolResults?: any[]; finishReason?: string; - usage?: { - promptTokens: number; - completionTokens: number; - totalTokens: number; - }; + usage?: any; + humanAction: string; + humanResult?: string; } interface AgentStepsState { - // Map of recognizedItemId to array of steps steps: Record; - // Actions - addStep: (recognizedItemId: string, step: Omit) => void; + addStep: (recognizedItemId: string, step: Omit) => void; + updateStep: (recognizedItemId: string, stepId: string, update: Partial) => void; updateStepResult: (recognizedItemId: string, stepId: string, result: string) => void; clearSteps: (recognizedItemId: string) => void; - clearAllSteps: () => void; } export const useAgentStepsStore = create((set) => ({ steps: {}, + addStep: (recognizedItemId, step) => set((state) => { + const newStep: AgentStep = { + id: crypto.randomUUID(), + timestamp: new Date().toISOString(), + ...step, + }; + return { + steps: { + ...state.steps, + [recognizedItemId]: [...(state.steps[recognizedItemId] || []), newStep], + }, + }; + }), + updateStep: (recognizedItemId, stepId, update) => set((state) => { + const steps = state.steps[recognizedItemId] || []; + const stepIndex = steps.findIndex(s => s.id === stepId); + if (stepIndex === -1) return state; - addStep: (recognizedItemId, step) => - set((state) => { - const currentSteps = state.steps[recognizedItemId] || []; - const newStep: AgentStep = { - ...step, - id: crypto.randomUUID(), - timestamp: Date.now(), - }; - - console.log('0xHypr', 'Adding agent step:', { - recognizedItemId, - step: newStep, - }); - - return { - steps: { - ...state.steps, - [recognizedItemId]: [...currentSteps, newStep], - }, - }; - }), + const updatedSteps = [...steps]; + updatedSteps[stepIndex] = { + ...updatedSteps[stepIndex], + ...update, + }; - updateStepResult: (recognizedItemId, stepId, result) => - set((state) => { - const currentSteps = state.steps[recognizedItemId] || []; - const updatedSteps = currentSteps.map(step => - step.id === stepId ? { ...step, humanResult: result } : step - ); + return { + steps: { + ...state.steps, + [recognizedItemId]: updatedSteps, + }, + }; + }), + updateStepResult: (recognizedItemId, stepId, result) => set((state) => { + const steps = state.steps[recognizedItemId] || []; + const stepIndex = steps.findIndex(s => s.id === stepId); + if (stepIndex === -1) return state; - return { - steps: { - ...state.steps, - [recognizedItemId]: updatedSteps, - }, - }; - }), + const updatedSteps = [...steps]; + updatedSteps[stepIndex] = { + ...updatedSteps[stepIndex], + humanResult: result, + }; - clearSteps: (recognizedItemId) => - set((state) => ({ + return { steps: { ...state.steps, - [recognizedItemId]: [], + [recognizedItemId]: updatedSteps, }, - })), - - clearAllSteps: () => - set({ - steps: {}, - }), + }; + }), + clearSteps: (recognizedItemId) => set((state) => ({ + steps: { + ...state.steps, + [recognizedItemId]: [], + }, + })), })); \ No newline at end of file diff --git a/pipes/auto-pay/src/types/wise.ts b/pipes/auto-pay/src/types/wise.ts index 7e618a6..491c9e5 100644 --- a/pipes/auto-pay/src/types/wise.ts +++ b/pipes/auto-pay/src/types/wise.ts @@ -16,6 +16,7 @@ export interface PaymentInfo { accountNumber: string; routingNumber: string; reference?: string; + recipientEmail?: string; } export interface WiseSettings {