chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+132
View File
@@ -0,0 +1,132 @@
import type {
AgentPhoneCreateCallParams,
AgentPhoneCreateCallResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneCreateCallTool: ToolConfig<
AgentPhoneCreateCallParams,
AgentPhoneCreateCallResult
> = {
id: 'agentphone_create_call',
name: 'Create Outbound Call',
description: 'Initiate an outbound voice call from an AgentPhone agent',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
agentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Agent that will handle the call',
},
toNumber: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Phone number to call in E.164 format (e.g. +14155551234)',
},
fromNumberId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
"Phone number ID to use as caller ID. Must belong to the agent. If omitted, the agent's first assigned number is used.",
},
initialGreeting: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional greeting spoken when the recipient answers',
},
voice: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Voice ID override for this call (defaults to the agent's configured voice)",
},
systemPrompt: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'When provided, uses a built-in LLM for the conversation instead of forwarding to your webhook',
},
},
request: {
url: 'https://api.agentphone.to/v1/calls',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
agentId: params.agentId,
toNumber: params.toNumber,
}
if (params.fromNumberId) body.fromNumberId = params.fromNumberId
if (params.initialGreeting) body.initialGreeting = params.initialGreeting
if (params.voice) body.voice = params.voice
if (params.systemPrompt) body.systemPrompt = params.systemPrompt
return body
},
},
transformResponse: async (response): Promise<AgentPhoneCreateCallResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to create call',
output: {
id: '',
agentId: null,
status: null,
toNumber: null,
fromNumber: null,
phoneNumberId: null,
direction: null,
startedAt: null,
},
}
}
return {
success: true,
output: {
id: data.id ?? data.callId ?? '',
agentId: data.agentId ?? null,
status: data.status ?? null,
toNumber: data.toNumber ?? null,
fromNumber: data.fromNumber ?? null,
phoneNumberId: data.phoneNumberId ?? null,
direction: data.direction ?? null,
startedAt: data.startedAt ?? null,
},
}
},
outputs: {
id: { type: 'string', description: 'Unique call identifier' },
agentId: { type: 'string', description: 'Agent handling the call', optional: true },
status: { type: 'string', description: 'Initial call status', optional: true },
toNumber: { type: 'string', description: 'Destination phone number', optional: true },
fromNumber: { type: 'string', description: 'Caller ID used for the call', optional: true },
phoneNumberId: {
type: 'string',
description: 'ID of the phone number used as caller ID',
optional: true,
},
direction: { type: 'string', description: 'Call direction (outbound)', optional: true },
startedAt: { type: 'string', description: 'ISO 8601 timestamp', optional: true },
},
}
+109
View File
@@ -0,0 +1,109 @@
import type {
AgentPhoneCreateContactParams,
AgentPhoneCreateContactResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneCreateContactTool: ToolConfig<
AgentPhoneCreateContactParams,
AgentPhoneCreateContactResult
> = {
id: 'agentphone_create_contact',
name: 'Create Contact',
description: 'Create a new contact in AgentPhone',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
phoneNumber: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Phone number in E.164 format (e.g. +14155551234)',
},
name: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: "Contact's full name",
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: "Contact's email address",
},
notes: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Freeform notes stored on the contact',
},
},
request: {
url: 'https://api.agentphone.to/v1/contacts',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
phoneNumber: params.phoneNumber,
name: params.name,
}
if (params.email) body.email = params.email
if (params.notes) body.notes = params.notes
return body
},
},
transformResponse: async (response): Promise<AgentPhoneCreateContactResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to create contact',
output: {
id: '',
phoneNumber: '',
name: '',
email: null,
notes: null,
createdAt: '',
updatedAt: '',
},
}
}
return {
success: true,
output: {
id: data.id ?? '',
phoneNumber: data.phoneNumber ?? '',
name: data.name ?? '',
email: data.email ?? null,
notes: data.notes ?? null,
createdAt: data.createdAt ?? '',
updatedAt: data.updatedAt ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Contact ID' },
phoneNumber: { type: 'string', description: 'Phone number in E.164 format' },
name: { type: 'string', description: 'Contact name' },
email: { type: 'string', description: 'Contact email address', optional: true },
notes: { type: 'string', description: 'Freeform notes', optional: true },
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
updatedAt: { type: 'string', description: 'ISO 8601 update timestamp' },
},
}
+106
View File
@@ -0,0 +1,106 @@
import type {
AgentPhoneCreateNumberParams,
AgentPhoneCreateNumberResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneCreateNumberTool: ToolConfig<
AgentPhoneCreateNumberParams,
AgentPhoneCreateNumberResult
> = {
id: 'agentphone_create_number',
name: 'Create Phone Number',
description: 'Provision a new SMS- and voice-enabled phone number',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
country: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Two-letter country code (e.g. US, CA). Defaults to US.',
},
areaCode: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
'Preferred area code (US/CA only, e.g. "415"). Best-effort — may be ignored if unavailable.',
},
agentId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optionally attach the number to an agent immediately',
},
},
request: {
url: 'https://api.agentphone.to/v1/numbers',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.country) body.country = params.country
if (params.areaCode) body.areaCode = params.areaCode
if (params.agentId) body.agentId = params.agentId
return body
},
},
transformResponse: async (response): Promise<AgentPhoneCreateNumberResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to create phone number',
output: {
id: '',
phoneNumber: '',
country: '',
status: '',
type: '',
agentId: null,
createdAt: '',
},
}
}
return {
success: true,
output: {
id: data.id ?? '',
phoneNumber: data.phoneNumber ?? '',
country: data.country ?? '',
status: data.status ?? '',
type: data.type ?? '',
agentId: data.agentId ?? null,
createdAt: data.createdAt ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Unique phone number ID' },
phoneNumber: { type: 'string', description: 'Provisioned phone number in E.164 format' },
country: { type: 'string', description: 'Two-letter country code' },
status: { type: 'string', description: 'Number status (e.g. active)' },
type: { type: 'string', description: 'Number type (e.g. sms)', optional: true },
agentId: {
type: 'string',
description: 'Agent the number is attached to',
optional: true,
},
createdAt: { type: 'string', description: 'ISO 8601 timestamp when the number was created' },
},
}
@@ -0,0 +1,67 @@
import type {
AgentPhoneDeleteContactParams,
AgentPhoneDeleteContactResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneDeleteContactTool: ToolConfig<
AgentPhoneDeleteContactParams,
AgentPhoneDeleteContactResult
> = {
id: 'agentphone_delete_contact',
name: 'Delete Contact',
description: 'Delete a contact by ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Contact ID',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/contacts/${params.contactId.trim()}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params): Promise<AgentPhoneDeleteContactResult> => {
const contactId = params?.contactId?.trim() ?? ''
if (!response.ok) {
let errorMessage = 'Failed to delete contact'
try {
const data = await response.json()
errorMessage = data?.detail?.[0]?.msg ?? data?.message ?? errorMessage
} catch {
// Response body may be empty; ignore parse failures.
}
return {
success: false,
error: errorMessage,
output: { id: contactId, deleted: false },
}
}
return {
success: true,
output: { id: contactId, deleted: true },
}
},
outputs: {
id: { type: 'string', description: 'ID of the deleted contact' },
deleted: { type: 'boolean', description: 'Whether the contact was deleted successfully' },
},
}
+146
View File
@@ -0,0 +1,146 @@
import type {
AgentPhoneGetCallParams,
AgentPhoneGetCallResult,
AgentPhoneTranscriptTurn,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetCallTool: ToolConfig<AgentPhoneGetCallParams, AgentPhoneGetCallResult> = {
id: 'agentphone_get_call',
name: 'Get Call',
description: 'Fetch a call and its full transcript',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
callId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the call to retrieve',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/calls/${params.callId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetCallResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch call',
output: {
id: '',
agentId: null,
phoneNumberId: null,
phoneNumber: null,
fromNumber: '',
toNumber: '',
direction: '',
status: '',
startedAt: null,
endedAt: null,
durationSeconds: null,
lastTranscriptSnippet: null,
recordingUrl: null,
recordingAvailable: null,
transcripts: [],
},
}
}
const transcripts: AgentPhoneTranscriptTurn[] = (data.transcripts ?? []).map(
(turn: Record<string, unknown>) => ({
id: (turn.id as string) ?? '',
transcript: (turn.transcript as string) ?? '',
confidence: (turn.confidence as number | null) ?? null,
response: (turn.response as string | null) ?? null,
createdAt: (turn.createdAt as string) ?? '',
})
)
return {
success: true,
output: {
id: data.id ?? '',
agentId: data.agentId ?? null,
phoneNumberId: data.phoneNumberId ?? null,
phoneNumber: data.phoneNumber ?? null,
fromNumber: data.fromNumber ?? '',
toNumber: data.toNumber ?? '',
direction: data.direction ?? '',
status: data.status ?? '',
startedAt: data.startedAt ?? null,
endedAt: data.endedAt ?? null,
durationSeconds: data.durationSeconds ?? null,
lastTranscriptSnippet: data.lastTranscriptSnippet ?? null,
recordingUrl: data.recordingUrl ?? null,
recordingAvailable: data.recordingAvailable ?? null,
transcripts,
},
}
},
outputs: {
id: { type: 'string', description: 'Call ID' },
agentId: { type: 'string', description: 'Agent that handled the call', optional: true },
phoneNumberId: { type: 'string', description: 'Phone number ID', optional: true },
phoneNumber: {
type: 'string',
description: 'Phone number used for the call',
optional: true,
},
fromNumber: { type: 'string', description: 'Caller phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
direction: { type: 'string', description: 'inbound or outbound', optional: true },
status: { type: 'string', description: 'Call status' },
startedAt: { type: 'string', description: 'ISO 8601 timestamp', optional: true },
endedAt: { type: 'string', description: 'ISO 8601 timestamp', optional: true },
durationSeconds: { type: 'number', description: 'Call duration in seconds', optional: true },
lastTranscriptSnippet: {
type: 'string',
description: 'Last transcript snippet',
optional: true,
},
recordingUrl: { type: 'string', description: 'Recording audio URL', optional: true },
recordingAvailable: {
type: 'boolean',
description: 'Whether a recording is available',
optional: true,
},
transcripts: {
type: 'array',
description: 'Ordered transcript turns for the call',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Transcript turn ID' },
transcript: { type: 'string', description: 'User utterance' },
confidence: {
type: 'number',
description: 'Speech recognition confidence',
optional: true,
},
response: {
type: 'string',
description: 'Agent response (when available)',
optional: true,
},
createdAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
},
}
@@ -0,0 +1,94 @@
import type {
AgentPhoneGetCallTranscriptParams,
AgentPhoneGetCallTranscriptResult,
AgentPhoneTranscriptEntry,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetCallTranscriptTool: ToolConfig<
AgentPhoneGetCallTranscriptParams,
AgentPhoneGetCallTranscriptResult
> = {
id: 'agentphone_get_call_transcript',
name: 'Get Call Transcript',
description: 'Get the full ordered transcript for a call',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
callId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the call to retrieve the transcript for',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/calls/${params.callId.trim()}/transcript`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params): Promise<AgentPhoneGetCallTranscriptResult> => {
const data = await response.json()
const callId = params?.callId?.trim() ?? ''
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch transcript',
output: { callId, transcript: [] },
}
}
const rawTurns = Array.isArray(data?.transcript)
? data.transcript
: Array.isArray(data)
? data
: []
const transcript: AgentPhoneTranscriptEntry[] = rawTurns.map(
(turn: Record<string, unknown>) => ({
role: (turn.role as string) ?? '',
content: (turn.content as string) ?? '',
createdAt: (turn.createdAt as string) ?? (turn.created_at as string) ?? null,
})
)
return {
success: true,
output: { callId: data?.callId ?? callId, transcript },
}
},
outputs: {
callId: { type: 'string', description: 'Call ID' },
transcript: {
type: 'array',
description: 'Ordered transcript turns for the call',
items: {
type: 'object',
properties: {
role: {
type: 'string',
description: 'Speaker role (user or agent)',
},
content: { type: 'string', description: 'Turn content' },
createdAt: {
type: 'string',
description: 'ISO 8601 timestamp',
optional: true,
},
},
},
},
},
}
+81
View File
@@ -0,0 +1,81 @@
import type {
AgentPhoneGetContactParams,
AgentPhoneGetContactResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetContactTool: ToolConfig<
AgentPhoneGetContactParams,
AgentPhoneGetContactResult
> = {
id: 'agentphone_get_contact',
name: 'Get Contact',
description: 'Fetch a single contact by ID',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Contact ID',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/contacts/${params.contactId.trim()}`,
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetContactResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch contact',
output: {
id: '',
phoneNumber: '',
name: '',
email: null,
notes: null,
createdAt: '',
updatedAt: '',
},
}
}
return {
success: true,
output: {
id: data.id ?? '',
phoneNumber: data.phoneNumber ?? '',
name: data.name ?? '',
email: data.email ?? null,
notes: data.notes ?? null,
createdAt: data.createdAt ?? '',
updatedAt: data.updatedAt ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Contact ID' },
phoneNumber: { type: 'string', description: 'Phone number in E.164 format' },
name: { type: 'string', description: 'Contact name' },
email: { type: 'string', description: 'Contact email address', optional: true },
notes: { type: 'string', description: 'Freeform notes', optional: true },
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
updatedAt: { type: 'string', description: 'ISO 8601 update timestamp' },
},
}
@@ -0,0 +1,143 @@
import type {
AgentPhoneConversationMessage,
AgentPhoneGetConversationParams,
AgentPhoneGetConversationResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetConversationTool: ToolConfig<
AgentPhoneGetConversationParams,
AgentPhoneGetConversationResult
> = {
id: 'agentphone_get_conversation',
name: 'Get Conversation',
description: 'Get a conversation along with its recent messages',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Conversation ID',
},
messageLimit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of recent messages to include (default 50, max 100)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.messageLimit === 'number') {
query.set('message_limit', String(params.messageLimit))
}
const qs = query.toString()
return `https://api.agentphone.to/v1/conversations/${params.conversationId.trim()}${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetConversationResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch conversation',
output: {
id: '',
agentId: null,
phoneNumberId: '',
phoneNumber: '',
participant: '',
lastMessageAt: '',
messageCount: 0,
metadata: null,
createdAt: '',
messages: [],
},
}
}
const messages: AgentPhoneConversationMessage[] = (data.messages ?? []).map(
(msg: Record<string, unknown>) => ({
id: (msg.id as string) ?? '',
body: (msg.body as string) ?? '',
fromNumber: (msg.fromNumber as string) ?? '',
toNumber: (msg.toNumber as string) ?? '',
direction: (msg.direction as string) ?? '',
channel: (msg.channel as string | null) ?? null,
mediaUrl: (msg.mediaUrl as string | null) ?? null,
mediaUrls: Array.isArray(msg.mediaUrls) ? (msg.mediaUrls as string[]) : [],
receivedAt: (msg.receivedAt as string) ?? '',
})
)
return {
success: true,
output: {
id: data.id ?? '',
agentId: data.agentId ?? null,
phoneNumberId: data.phoneNumberId ?? '',
phoneNumber: data.phoneNumber ?? '',
participant: data.participant ?? '',
lastMessageAt: data.lastMessageAt ?? '',
messageCount: data.messageCount ?? 0,
metadata: data.metadata ?? null,
createdAt: data.createdAt ?? '',
messages,
},
}
},
outputs: {
id: { type: 'string', description: 'Conversation ID' },
agentId: { type: 'string', description: 'Agent ID', optional: true },
phoneNumberId: { type: 'string', description: 'Phone number ID' },
phoneNumber: { type: 'string', description: 'Phone number' },
participant: { type: 'string', description: 'External participant phone number' },
lastMessageAt: { type: 'string', description: 'ISO 8601 timestamp' },
messageCount: { type: 'number', description: 'Number of messages in the conversation' },
metadata: {
type: 'json',
description: 'Custom metadata stored on the conversation',
optional: true,
},
createdAt: { type: 'string', description: 'ISO 8601 timestamp' },
messages: {
type: 'array',
description: 'Recent messages in the conversation',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Message ID' },
body: { type: 'string', description: 'Message text' },
fromNumber: { type: 'string', description: 'Sender phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
direction: { type: 'string', description: 'inbound or outbound' },
channel: { type: 'string', description: 'sms, mms, or imessage', optional: true },
mediaUrl: { type: 'string', description: 'Attached media URL', optional: true },
mediaUrls: {
type: 'array',
description: 'All attached media URLs',
items: { type: 'string' },
},
receivedAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
},
}
@@ -0,0 +1,122 @@
import type {
AgentPhoneConversationMessage,
AgentPhoneGetConversationMessagesParams,
AgentPhoneGetConversationMessagesResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetConversationMessagesTool: ToolConfig<
AgentPhoneGetConversationMessagesParams,
AgentPhoneGetConversationMessagesResult
> = {
id: 'agentphone_get_conversation_messages',
name: 'Get Conversation Messages',
description: 'Get paginated messages for a conversation',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Conversation ID',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of messages to return (default 50, max 200)',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Return messages received before this ISO 8601 timestamp',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Return messages received after this ISO 8601 timestamp',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (params.before) query.set('before', params.before)
if (params.after) query.set('after', params.after)
const qs = query.toString()
return `https://api.agentphone.to/v1/conversations/${params.conversationId.trim()}/messages${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetConversationMessagesResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch messages',
output: { data: [], hasMore: false },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(msg: Record<string, unknown>): AgentPhoneConversationMessage => ({
id: (msg.id as string) ?? '',
body: (msg.body as string) ?? '',
fromNumber: (msg.fromNumber as string) ?? '',
toNumber: (msg.toNumber as string) ?? '',
direction: (msg.direction as string) ?? '',
channel: (msg.channel as string | null) ?? null,
mediaUrl: (msg.mediaUrl as string | null) ?? null,
mediaUrls: Array.isArray(msg.mediaUrls) ? (msg.mediaUrls as string[]) : [],
receivedAt: (msg.receivedAt as string) ?? '',
})
),
hasMore: data.hasMore ?? false,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Messages in the conversation',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Message ID' },
body: { type: 'string', description: 'Message text' },
fromNumber: { type: 'string', description: 'Sender phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
direction: { type: 'string', description: 'inbound or outbound' },
channel: { type: 'string', description: 'sms, mms, or imessage', optional: true },
mediaUrl: { type: 'string', description: 'Attached media URL', optional: true },
mediaUrls: {
type: 'array',
description: 'All attached media URLs',
items: { type: 'string' },
},
receivedAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
hasMore: { type: 'boolean', description: 'Whether more messages are available' },
},
}
@@ -0,0 +1,115 @@
import type {
AgentPhoneGetNumberMessagesParams,
AgentPhoneGetNumberMessagesResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetNumberMessagesTool: ToolConfig<
AgentPhoneGetNumberMessagesParams,
AgentPhoneGetNumberMessagesResult
> = {
id: 'agentphone_get_number_messages',
name: 'Get Phone Number Messages',
description: 'Fetch messages received on a specific phone number',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
numberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the phone number',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of messages to return (default 50, max 200)',
},
before: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Return messages received before this ISO 8601 timestamp',
},
after: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Return messages received after this ISO 8601 timestamp',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (params.before) query.set('before', params.before)
if (params.after) query.set('after', params.after)
const qs = query.toString()
return `https://api.agentphone.to/v1/numbers/${params.numberId.trim()}/messages${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetNumberMessagesResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch messages',
output: { data: [], hasMore: false },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map((msg: Record<string, unknown>) => ({
id: (msg.id as string) ?? '',
from_: (msg.from_ as string) ?? '',
to: (msg.to as string) ?? '',
body: (msg.body as string) ?? '',
direction: (msg.direction as string) ?? '',
channel: (msg.channel as string | null) ?? null,
receivedAt: (msg.receivedAt as string) ?? '',
})),
hasMore: data.hasMore ?? false,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Messages received on the number',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Message ID' },
from_: { type: 'string', description: 'Sender phone number (E.164)' },
to: { type: 'string', description: 'Recipient phone number (E.164)' },
body: { type: 'string', description: 'Message text' },
direction: { type: 'string', description: 'inbound or outbound' },
channel: {
type: 'string',
description: 'Channel (sms, mms, etc.)',
optional: true,
},
receivedAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
hasMore: { type: 'boolean', description: 'Whether more messages are available' },
},
}
+127
View File
@@ -0,0 +1,127 @@
import type { AgentPhoneGetUsageParams, AgentPhoneGetUsageResult } from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetUsageTool: ToolConfig<
AgentPhoneGetUsageParams,
AgentPhoneGetUsageResult
> = {
id: 'agentphone_get_usage',
name: 'Get Usage',
description: 'Retrieve current usage statistics for the AgentPhone account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
},
request: {
url: 'https://api.agentphone.to/v1/usage',
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetUsageResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch usage',
output: {
plan: {
name: '',
limits: {
numbers: null,
messagesPerMonth: null,
voiceMinutesPerMonth: null,
maxCallDurationMinutes: null,
concurrentCalls: null,
},
},
numbers: { used: null, limit: null, remaining: null },
stats: {
totalMessages: null,
messagesLast24h: null,
messagesLast7d: null,
messagesLast30d: null,
totalCalls: null,
callsLast24h: null,
callsLast7d: null,
callsLast30d: null,
totalWebhookDeliveries: null,
successfulWebhookDeliveries: null,
failedWebhookDeliveries: null,
},
periodStart: '',
periodEnd: '',
},
}
}
const planLimits = data?.plan?.limits ?? {}
const numbers = data?.numbers ?? {}
const stats = data?.stats ?? {}
return {
success: true,
output: {
plan: {
name: data?.plan?.name ?? '',
limits: {
numbers: planLimits.numbers ?? null,
messagesPerMonth: planLimits.messagesPerMonth ?? null,
voiceMinutesPerMonth: planLimits.voiceMinutesPerMonth ?? null,
maxCallDurationMinutes: planLimits.maxCallDurationMinutes ?? null,
concurrentCalls: planLimits.concurrentCalls ?? null,
},
},
numbers: {
used: numbers.used ?? null,
limit: numbers.limit ?? null,
remaining: numbers.remaining ?? null,
},
stats: {
totalMessages: stats.totalMessages ?? null,
messagesLast24h: stats.messagesLast24h ?? null,
messagesLast7d: stats.messagesLast7d ?? null,
messagesLast30d: stats.messagesLast30d ?? null,
totalCalls: stats.totalCalls ?? null,
callsLast24h: stats.callsLast24h ?? null,
callsLast7d: stats.callsLast7d ?? null,
callsLast30d: stats.callsLast30d ?? null,
totalWebhookDeliveries: stats.totalWebhookDeliveries ?? null,
successfulWebhookDeliveries: stats.successfulWebhookDeliveries ?? null,
failedWebhookDeliveries: stats.failedWebhookDeliveries ?? null,
},
periodStart: data.periodStart ?? '',
periodEnd: data.periodEnd ?? '',
},
}
},
outputs: {
plan: {
type: 'json',
description:
'Plan name and limits (name, limits: numbers/messagesPerMonth/voiceMinutesPerMonth/maxCallDurationMinutes/concurrentCalls)',
},
numbers: {
type: 'json',
description: 'Phone number usage (used, limit, remaining)',
},
stats: {
type: 'json',
description:
'Usage stats: totalMessages, messagesLast24h/7d/30d, totalCalls, callsLast24h/7d/30d, totalWebhookDeliveries, successfulWebhookDeliveries, failedWebhookDeliveries',
},
periodStart: { type: 'string', description: 'Billing period start' },
periodEnd: { type: 'string', description: 'Billing period end' },
},
}
@@ -0,0 +1,88 @@
import type {
AgentPhoneGetUsageDailyParams,
AgentPhoneGetUsageDailyResult,
AgentPhoneUsageDailyEntry,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetUsageDailyTool: ToolConfig<
AgentPhoneGetUsageDailyParams,
AgentPhoneGetUsageDailyResult
> = {
id: 'agentphone_get_usage_daily',
name: 'Get Daily Usage',
description: 'Get a daily breakdown of usage (messages, calls, webhooks) for the last N days',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
days: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of days to return (1-365, default 30)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.days === 'number') query.set('days', String(params.days))
const qs = query.toString()
return `https://api.agentphone.to/v1/usage/daily${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetUsageDailyResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch daily usage',
output: { data: [], days: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(entry: Record<string, unknown>): AgentPhoneUsageDailyEntry => ({
date: (entry.date as string) ?? '',
messages: (entry.messages as number) ?? 0,
calls: (entry.calls as number) ?? 0,
webhooks: (entry.webhooks as number) ?? 0,
})
),
days: data.days ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Daily usage entries',
items: {
type: 'object',
properties: {
date: { type: 'string', description: 'Day (YYYY-MM-DD)' },
messages: { type: 'number', description: 'Messages that day' },
calls: { type: 'number', description: 'Calls that day' },
webhooks: { type: 'number', description: 'Webhook deliveries that day' },
},
},
},
days: { type: 'number', description: 'Number of days returned' },
},
}
@@ -0,0 +1,88 @@
import type {
AgentPhoneGetUsageMonthlyParams,
AgentPhoneGetUsageMonthlyResult,
AgentPhoneUsageMonthlyEntry,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneGetUsageMonthlyTool: ToolConfig<
AgentPhoneGetUsageMonthlyParams,
AgentPhoneGetUsageMonthlyResult
> = {
id: 'agentphone_get_usage_monthly',
name: 'Get Monthly Usage',
description: 'Get monthly usage aggregation (messages, calls, webhooks) for the last N months',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
months: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of months to return (1-24, default 6)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.months === 'number') query.set('months', String(params.months))
const qs = query.toString()
return `https://api.agentphone.to/v1/usage/monthly${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneGetUsageMonthlyResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to fetch monthly usage',
output: { data: [], months: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(entry: Record<string, unknown>): AgentPhoneUsageMonthlyEntry => ({
month: (entry.month as string) ?? '',
messages: (entry.messages as number) ?? 0,
calls: (entry.calls as number) ?? 0,
webhooks: (entry.webhooks as number) ?? 0,
})
),
months: data.months ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Monthly usage entries',
items: {
type: 'object',
properties: {
month: { type: 'string', description: 'Month (YYYY-MM)' },
messages: { type: 'number', description: 'Messages that month' },
calls: { type: 'number', description: 'Calls that month' },
webhooks: { type: 'number', description: 'Webhook deliveries that month' },
},
},
},
months: { type: 'number', description: 'Number of months returned' },
},
}
+23
View File
@@ -0,0 +1,23 @@
export { agentphoneCreateCallTool } from '@/tools/agentphone/create_call'
export { agentphoneCreateContactTool } from '@/tools/agentphone/create_contact'
export { agentphoneCreateNumberTool } from '@/tools/agentphone/create_number'
export { agentphoneDeleteContactTool } from '@/tools/agentphone/delete_contact'
export { agentphoneGetCallTool } from '@/tools/agentphone/get_call'
export { agentphoneGetCallTranscriptTool } from '@/tools/agentphone/get_call_transcript'
export { agentphoneGetContactTool } from '@/tools/agentphone/get_contact'
export { agentphoneGetConversationTool } from '@/tools/agentphone/get_conversation'
export { agentphoneGetConversationMessagesTool } from '@/tools/agentphone/get_conversation_messages'
export { agentphoneGetNumberMessagesTool } from '@/tools/agentphone/get_number_messages'
export { agentphoneGetUsageTool } from '@/tools/agentphone/get_usage'
export { agentphoneGetUsageDailyTool } from '@/tools/agentphone/get_usage_daily'
export { agentphoneGetUsageMonthlyTool } from '@/tools/agentphone/get_usage_monthly'
export { agentphoneListCallsTool } from '@/tools/agentphone/list_calls'
export { agentphoneListContactsTool } from '@/tools/agentphone/list_contacts'
export { agentphoneListConversationsTool } from '@/tools/agentphone/list_conversations'
export { agentphoneListNumbersTool } from '@/tools/agentphone/list_numbers'
export { agentphoneReactToMessageTool } from '@/tools/agentphone/react_to_message'
export { agentphoneReleaseNumberTool } from '@/tools/agentphone/release_number'
export { agentphoneSendMessageTool } from '@/tools/agentphone/send_message'
export * from '@/tools/agentphone/types'
export { agentphoneUpdateContactTool } from '@/tools/agentphone/update_contact'
export { agentphoneUpdateConversationTool } from '@/tools/agentphone/update_conversation'
+169
View File
@@ -0,0 +1,169 @@
import type {
AgentPhoneCallSummary,
AgentPhoneListCallsParams,
AgentPhoneListCallsResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneListCallsTool: ToolConfig<
AgentPhoneListCallsParams,
AgentPhoneListCallsResult
> = {
id: 'agentphone_list_calls',
name: 'List Calls',
description: 'List voice calls for this AgentPhone account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (default 20, max 100)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to skip (min 0)',
},
status: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by status (completed, in-progress, failed)',
},
direction: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by direction (inbound, outbound)',
},
type: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by call type (pstn, web)',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Search by phone number (matches fromNumber or toNumber)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (typeof params.offset === 'number') query.set('offset', String(params.offset))
if (params.status) query.set('status', params.status)
if (params.direction) query.set('direction', params.direction)
if (params.type) query.set('type', params.type)
if (params.search) query.set('search', params.search)
const qs = query.toString()
return `https://api.agentphone.to/v1/calls${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneListCallsResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to list calls',
output: { data: [], hasMore: false, total: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(call: Record<string, unknown>): AgentPhoneCallSummary => ({
id: (call.id as string) ?? '',
agentId: (call.agentId as string | null) ?? null,
phoneNumberId: (call.phoneNumberId as string | null) ?? null,
phoneNumber: (call.phoneNumber as string | null) ?? null,
fromNumber: (call.fromNumber as string) ?? '',
toNumber: (call.toNumber as string) ?? '',
direction: (call.direction as string) ?? '',
status: (call.status as string) ?? '',
startedAt: (call.startedAt as string | null) ?? null,
endedAt: (call.endedAt as string | null) ?? null,
durationSeconds: (call.durationSeconds as number | null) ?? null,
lastTranscriptSnippet: (call.lastTranscriptSnippet as string | null) ?? null,
recordingUrl: (call.recordingUrl as string | null) ?? null,
recordingAvailable: (call.recordingAvailable as boolean | null) ?? null,
})
),
hasMore: data.hasMore ?? false,
total: data.total ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Calls',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Call ID' },
agentId: {
type: 'string',
description: 'Agent that handled the call',
optional: true,
},
phoneNumberId: {
type: 'string',
description: 'Phone number ID used for the call',
optional: true,
},
phoneNumber: {
type: 'string',
description: 'Phone number used for the call',
optional: true,
},
fromNumber: { type: 'string', description: 'Caller phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
direction: { type: 'string', description: 'inbound or outbound', optional: true },
status: { type: 'string', description: 'Call status' },
startedAt: { type: 'string', description: 'ISO 8601 timestamp', optional: true },
endedAt: { type: 'string', description: 'ISO 8601 timestamp', optional: true },
durationSeconds: {
type: 'number',
description: 'Call duration in seconds',
optional: true,
},
lastTranscriptSnippet: {
type: 'string',
description: 'Last transcript snippet',
optional: true,
},
recordingUrl: { type: 'string', description: 'Recording audio URL', optional: true },
recordingAvailable: {
type: 'boolean',
description: 'Whether a recording is available',
optional: true,
},
},
},
},
hasMore: { type: 'boolean', description: 'Whether more results are available' },
total: { type: 'number', description: 'Total number of matching calls' },
},
}
+110
View File
@@ -0,0 +1,110 @@
import type {
AgentPhoneContact,
AgentPhoneListContactsParams,
AgentPhoneListContactsResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneListContactsTool: ToolConfig<
AgentPhoneListContactsParams,
AgentPhoneListContactsResult
> = {
id: 'agentphone_list_contacts',
name: 'List Contacts',
description: 'List contacts for this AgentPhone account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
search: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Filter by name or phone number (case-insensitive contains)',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (default 50, max 200)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to skip (min 0)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (params.search) query.set('search', params.search)
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (typeof params.offset === 'number') query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.agentphone.to/v1/contacts${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneListContactsResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to list contacts',
output: { data: [], hasMore: false, total: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(c: Record<string, unknown>): AgentPhoneContact => ({
id: (c.id as string) ?? '',
phoneNumber: (c.phoneNumber as string) ?? '',
name: (c.name as string) ?? '',
email: (c.email as string | null) ?? null,
notes: (c.notes as string | null) ?? null,
createdAt: (c.createdAt as string) ?? '',
updatedAt: (c.updatedAt as string) ?? '',
})
),
hasMore: data.hasMore ?? false,
total: data.total ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Contacts',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Contact ID' },
phoneNumber: { type: 'string', description: 'Phone number in E.164 format' },
name: { type: 'string', description: 'Contact name' },
email: { type: 'string', description: 'Contact email address', optional: true },
notes: { type: 'string', description: 'Freeform notes', optional: true },
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
updatedAt: { type: 'string', description: 'ISO 8601 update timestamp' },
},
},
},
hasMore: { type: 'boolean', description: 'Whether more results are available' },
total: { type: 'number', description: 'Total number of contacts' },
},
}
@@ -0,0 +1,113 @@
import type {
AgentPhoneConversationSummary,
AgentPhoneListConversationsParams,
AgentPhoneListConversationsResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneListConversationsTool: ToolConfig<
AgentPhoneListConversationsParams,
AgentPhoneListConversationsResult
> = {
id: 'agentphone_list_conversations',
name: 'List Conversations',
description: 'List conversations (message threads) for this AgentPhone account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (default 20, max 100)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to skip (min 0)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (typeof params.offset === 'number') query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.agentphone.to/v1/conversations${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneListConversationsResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to list conversations',
output: { data: [], hasMore: false, total: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map(
(conv: Record<string, unknown>): AgentPhoneConversationSummary => ({
id: (conv.id as string) ?? '',
agentId: (conv.agentId as string | null) ?? null,
phoneNumberId: (conv.phoneNumberId as string) ?? '',
phoneNumber: (conv.phoneNumber as string) ?? '',
participant: (conv.participant as string) ?? '',
lastMessageAt: (conv.lastMessageAt as string) ?? '',
lastMessagePreview: (conv.lastMessagePreview as string) ?? '',
messageCount: (conv.messageCount as number) ?? 0,
metadata: (conv.metadata as Record<string, unknown> | null) ?? null,
createdAt: (conv.createdAt as string) ?? '',
})
),
hasMore: data.hasMore ?? false,
total: data.total ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Conversations',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Conversation ID' },
agentId: { type: 'string', description: 'Agent ID', optional: true },
phoneNumberId: { type: 'string', description: 'Phone number ID' },
phoneNumber: { type: 'string', description: 'Phone number' },
participant: { type: 'string', description: 'External participant phone number' },
lastMessageAt: { type: 'string', description: 'ISO 8601 timestamp' },
lastMessagePreview: { type: 'string', description: 'Last message preview' },
messageCount: { type: 'number', description: 'Number of messages in the conversation' },
metadata: {
type: 'json',
description: 'Custom metadata stored on the conversation',
optional: true,
},
createdAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
hasMore: { type: 'boolean', description: 'Whether more results are available' },
total: { type: 'number', description: 'Total number of conversations' },
},
}
+100
View File
@@ -0,0 +1,100 @@
import type {
AgentPhoneListNumbersParams,
AgentPhoneListNumbersResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneListNumbersTool: ToolConfig<
AgentPhoneListNumbersParams,
AgentPhoneListNumbersResult
> = {
id: 'agentphone_list_numbers',
name: 'List Phone Numbers',
description: 'List all phone numbers provisioned for this AgentPhone account',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
limit: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to return (default 20, max 100)',
},
offset: {
type: 'number',
required: false,
visibility: 'user-or-llm',
description: 'Number of results to skip (min 0)',
},
},
request: {
url: (params) => {
const query = new URLSearchParams()
if (typeof params.limit === 'number') query.set('limit', String(params.limit))
if (typeof params.offset === 'number') query.set('offset', String(params.offset))
const qs = query.toString()
return `https://api.agentphone.to/v1/numbers${qs ? `?${qs}` : ''}`
},
method: 'GET',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response): Promise<AgentPhoneListNumbersResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to list phone numbers',
output: { data: [], hasMore: false, total: 0 },
}
}
return {
success: true,
output: {
data: (data.data ?? []).map((num: Record<string, unknown>) => ({
id: (num.id as string) ?? '',
phoneNumber: (num.phoneNumber as string) ?? '',
country: (num.country as string) ?? '',
status: (num.status as string) ?? '',
type: (num.type as string) ?? '',
agentId: (num.agentId as string | null) ?? null,
createdAt: (num.createdAt as string) ?? '',
})),
hasMore: data.hasMore ?? false,
total: data.total ?? 0,
},
}
},
outputs: {
data: {
type: 'array',
description: 'Phone numbers',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Phone number ID' },
phoneNumber: { type: 'string', description: 'Phone number in E.164 format' },
country: { type: 'string', description: 'Two-letter country code' },
status: { type: 'string', description: 'Number status' },
type: { type: 'string', description: 'Number type (e.g. sms)', optional: true },
agentId: { type: 'string', description: 'Attached agent ID', optional: true },
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
},
},
},
hasMore: { type: 'boolean', description: 'Whether more results are available' },
total: { type: 'number', description: 'Total number of phone numbers' },
},
}
@@ -0,0 +1,76 @@
import type {
AgentPhoneReactToMessageParams,
AgentPhoneReactToMessageResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneReactToMessageTool: ToolConfig<
AgentPhoneReactToMessageParams,
AgentPhoneReactToMessageResult
> = {
id: 'agentphone_react_to_message',
name: 'React to Message',
description: 'Send an iMessage tapback reaction to a message (iMessage only)',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
messageId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the message to react to',
},
reaction: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Reaction type: love, like, dislike, laugh, emphasize, or question',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/messages/${params.messageId.trim()}/reactions`,
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => ({ reaction: params.reaction }),
},
transformResponse: async (response, params): Promise<AgentPhoneReactToMessageResult> => {
const data = await response.json()
const messageId = params?.messageId?.trim() ?? ''
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to send reaction',
output: { id: '', reactionType: '', messageId, channel: '' },
}
}
return {
success: true,
output: {
id: data.id ?? '',
reactionType: data.reaction_type ?? '',
messageId: data.message_id ?? messageId,
channel: data.channel ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Reaction ID' },
reactionType: { type: 'string', description: 'Reaction type applied' },
messageId: { type: 'string', description: 'ID of the message that was reacted to' },
channel: { type: 'string', description: 'Channel (imessage)' },
},
}
@@ -0,0 +1,67 @@
import type {
AgentPhoneReleaseNumberParams,
AgentPhoneReleaseNumberResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneReleaseNumberTool: ToolConfig<
AgentPhoneReleaseNumberParams,
AgentPhoneReleaseNumberResult
> = {
id: 'agentphone_release_number',
name: 'Release Phone Number',
description: 'Release (delete) a phone number. This action is irreversible.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
numberId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'ID of the phone number to release',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/numbers/${params.numberId.trim()}`,
method: 'DELETE',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
}),
},
transformResponse: async (response, params): Promise<AgentPhoneReleaseNumberResult> => {
const numberId = params?.numberId?.trim() ?? ''
if (!response.ok) {
let errorMessage = 'Failed to release phone number'
try {
const data = await response.json()
errorMessage = data?.detail?.[0]?.msg ?? data?.message ?? errorMessage
} catch {
// Response body may be empty on DELETE errors; ignore parse failures.
}
return {
success: false,
error: errorMessage,
output: { id: numberId, released: false },
}
}
return {
success: true,
output: { id: numberId, released: true },
}
},
outputs: {
id: { type: 'string', description: 'ID of the released phone number' },
released: { type: 'boolean', description: 'Whether the number was released successfully' },
},
}
+105
View File
@@ -0,0 +1,105 @@
import type {
AgentPhoneSendMessageParams,
AgentPhoneSendMessageResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneSendMessageTool: ToolConfig<
AgentPhoneSendMessageParams,
AgentPhoneSendMessageResult
> = {
id: 'agentphone_send_message',
name: 'Send Message',
description: 'Send an outbound SMS or iMessage from an AgentPhone agent',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
agentId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Agent sending the message',
},
toNumber: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Recipient phone number in E.164 format (e.g. +14155551234)',
},
body: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Message text to send',
},
mediaUrl: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'Optional URL of an image, video, or file to attach',
},
numberId: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description:
"Phone number ID to send from. If omitted, the agent's first assigned number is used.",
},
},
request: {
url: 'https://api.agentphone.to/v1/messages',
method: 'POST',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {
agent_id: params.agentId,
to_number: params.toNumber,
body: params.body,
}
if (params.mediaUrl) body.media_url = params.mediaUrl
if (params.numberId) body.number_id = params.numberId
return body
},
},
transformResponse: async (response): Promise<AgentPhoneSendMessageResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to send message',
output: { id: '', status: '', channel: '', fromNumber: '', toNumber: '' },
}
}
return {
success: true,
output: {
id: data.id ?? '',
status: data.status ?? '',
channel: data.channel ?? '',
fromNumber: data.from_number ?? '',
toNumber: data.to_number ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Message ID' },
status: { type: 'string', description: 'Delivery status' },
channel: { type: 'string', description: 'sms, mms, or imessage' },
fromNumber: { type: 'string', description: 'Sender phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
},
}
+451
View File
@@ -0,0 +1,451 @@
import type { ToolResponse } from '@/tools/types'
interface AgentPhoneNumber {
id: string
phoneNumber: string
country: string
status: string
type: string
agentId: string | null
createdAt: string
}
interface AgentPhoneNumberMessage {
id: string
from_: string
to: string
body: string
direction: string
channel: string | null
receivedAt: string
}
export interface AgentPhoneConversationSummary {
id: string
agentId: string | null
phoneNumberId: string
phoneNumber: string
participant: string
lastMessageAt: string
lastMessagePreview: string
messageCount: number
metadata: Record<string, unknown> | null
createdAt: string
}
export interface AgentPhoneConversationMessage {
id: string
body: string
fromNumber: string
toNumber: string
direction: string
channel: string | null
mediaUrl: string | null
mediaUrls: string[]
receivedAt: string
}
interface AgentPhoneConversationDetail {
id: string
agentId: string | null
phoneNumberId: string
phoneNumber: string
participant: string
lastMessageAt: string
messageCount: number
metadata: Record<string, unknown> | null
createdAt: string
messages: AgentPhoneConversationMessage[]
}
export interface AgentPhoneCallSummary {
id: string
agentId: string | null
phoneNumberId: string | null
phoneNumber: string | null
fromNumber: string
toNumber: string
direction: string
status: string
startedAt: string | null
endedAt: string | null
durationSeconds: number | null
lastTranscriptSnippet: string | null
recordingUrl: string | null
recordingAvailable: boolean | null
}
export interface AgentPhoneTranscriptTurn {
id: string
transcript: string
confidence: number | null
response: string | null
createdAt: string
}
export interface AgentPhoneTranscriptEntry {
role: string
content: string
createdAt: string | null
}
interface AgentPhoneCallDetail extends AgentPhoneCallSummary {
transcripts: AgentPhoneTranscriptTurn[]
}
export interface AgentPhoneContact {
id: string
phoneNumber: string
name: string
email: string | null
notes: string | null
createdAt: string
updatedAt: string
}
export interface AgentPhoneCreateNumberParams {
apiKey: string
country?: string
areaCode?: string
agentId?: string
}
export interface AgentPhoneCreateNumberResult extends ToolResponse {
output: AgentPhoneNumber
}
export interface AgentPhoneListNumbersParams {
apiKey: string
limit?: number
offset?: number
}
export interface AgentPhoneListNumbersResult extends ToolResponse {
output: {
data: AgentPhoneNumber[]
hasMore: boolean
total: number
}
}
export interface AgentPhoneReleaseNumberParams {
apiKey: string
numberId: string
}
export interface AgentPhoneReleaseNumberResult extends ToolResponse {
output: {
id: string
released: boolean
}
}
export interface AgentPhoneGetNumberMessagesParams {
apiKey: string
numberId: string
limit?: number
before?: string
after?: string
}
export interface AgentPhoneGetNumberMessagesResult extends ToolResponse {
output: {
data: AgentPhoneNumberMessage[]
hasMore: boolean
}
}
export interface AgentPhoneCreateCallParams {
apiKey: string
agentId: string
toNumber: string
fromNumberId?: string
initialGreeting?: string
voice?: string
systemPrompt?: string
}
export interface AgentPhoneCreateCallResult extends ToolResponse {
output: {
id: string
agentId: string | null
status: string | null
toNumber: string | null
fromNumber: string | null
phoneNumberId: string | null
direction: string | null
startedAt: string | null
}
}
export interface AgentPhoneListCallsParams {
apiKey: string
limit?: number
offset?: number
status?: string
direction?: string
type?: string
search?: string
}
export interface AgentPhoneListCallsResult extends ToolResponse {
output: {
data: AgentPhoneCallSummary[]
hasMore: boolean
total: number
}
}
export interface AgentPhoneGetCallParams {
apiKey: string
callId: string
}
export interface AgentPhoneGetCallResult extends ToolResponse {
output: AgentPhoneCallDetail
}
export interface AgentPhoneGetCallTranscriptParams {
apiKey: string
callId: string
}
export interface AgentPhoneGetCallTranscriptResult extends ToolResponse {
output: {
callId: string
transcript: AgentPhoneTranscriptEntry[]
}
}
export interface AgentPhoneListConversationsParams {
apiKey: string
limit?: number
offset?: number
}
export interface AgentPhoneListConversationsResult extends ToolResponse {
output: {
data: AgentPhoneConversationSummary[]
hasMore: boolean
total: number
}
}
export interface AgentPhoneGetConversationParams {
apiKey: string
conversationId: string
messageLimit?: number
}
export interface AgentPhoneGetConversationResult extends ToolResponse {
output: AgentPhoneConversationDetail
}
export interface AgentPhoneUpdateConversationParams {
apiKey: string
conversationId: string
metadata?: Record<string, unknown> | null
}
export interface AgentPhoneUpdateConversationResult extends ToolResponse {
output: AgentPhoneConversationDetail
}
export interface AgentPhoneGetConversationMessagesParams {
apiKey: string
conversationId: string
limit?: number
before?: string
after?: string
}
export interface AgentPhoneGetConversationMessagesResult extends ToolResponse {
output: {
data: AgentPhoneConversationMessage[]
hasMore: boolean
}
}
export interface AgentPhoneSendMessageParams {
apiKey: string
agentId: string
toNumber: string
body: string
mediaUrl?: string
numberId?: string
}
export interface AgentPhoneSendMessageResult extends ToolResponse {
output: {
id: string
status: string
channel: string
fromNumber: string
toNumber: string
}
}
export type AgentPhoneReactionType =
| 'love'
| 'like'
| 'dislike'
| 'laugh'
| 'emphasize'
| 'question'
export interface AgentPhoneReactToMessageParams {
apiKey: string
messageId: string
reaction: AgentPhoneReactionType
}
export interface AgentPhoneReactToMessageResult extends ToolResponse {
output: {
id: string
reactionType: string
messageId: string
channel: string
}
}
export interface AgentPhoneCreateContactParams {
apiKey: string
phoneNumber: string
name: string
email?: string
notes?: string
}
export interface AgentPhoneCreateContactResult extends ToolResponse {
output: AgentPhoneContact
}
export interface AgentPhoneListContactsParams {
apiKey: string
search?: string
limit?: number
offset?: number
}
export interface AgentPhoneListContactsResult extends ToolResponse {
output: {
data: AgentPhoneContact[]
hasMore: boolean
total: number
}
}
export interface AgentPhoneGetContactParams {
apiKey: string
contactId: string
}
export interface AgentPhoneGetContactResult extends ToolResponse {
output: AgentPhoneContact
}
export interface AgentPhoneUpdateContactParams {
apiKey: string
contactId: string
phoneNumber?: string
name?: string
email?: string
notes?: string
}
export interface AgentPhoneUpdateContactResult extends ToolResponse {
output: AgentPhoneContact
}
export interface AgentPhoneDeleteContactParams {
apiKey: string
contactId: string
}
export interface AgentPhoneDeleteContactResult extends ToolResponse {
output: {
id: string
deleted: boolean
}
}
interface AgentPhoneUsagePlan {
name: string
limits: {
numbers: number | null
messagesPerMonth: number | null
voiceMinutesPerMonth: number | null
maxCallDurationMinutes: number | null
concurrentCalls: number | null
}
}
interface AgentPhoneUsageStats {
totalMessages: number | null
messagesLast24h: number | null
messagesLast7d: number | null
messagesLast30d: number | null
totalCalls: number | null
callsLast24h: number | null
callsLast7d: number | null
callsLast30d: number | null
totalWebhookDeliveries: number | null
successfulWebhookDeliveries: number | null
failedWebhookDeliveries: number | null
}
export interface AgentPhoneGetUsageParams {
apiKey: string
}
export interface AgentPhoneGetUsageResult extends ToolResponse {
output: {
plan: AgentPhoneUsagePlan
numbers: {
used: number | null
limit: number | null
remaining: number | null
}
stats: AgentPhoneUsageStats
periodStart: string
periodEnd: string
}
}
export interface AgentPhoneUsageDailyEntry {
date: string
messages: number
calls: number
webhooks: number
}
export interface AgentPhoneGetUsageDailyParams {
apiKey: string
days?: number
}
export interface AgentPhoneGetUsageDailyResult extends ToolResponse {
output: {
data: AgentPhoneUsageDailyEntry[]
days: number
}
}
export interface AgentPhoneUsageMonthlyEntry {
month: string
messages: number
calls: number
webhooks: number
}
export interface AgentPhoneGetUsageMonthlyParams {
apiKey: string
months?: number
}
export interface AgentPhoneGetUsageMonthlyResult extends ToolResponse {
output: {
data: AgentPhoneUsageMonthlyEntry[]
months: number
}
}
+114
View File
@@ -0,0 +1,114 @@
import type {
AgentPhoneUpdateContactParams,
AgentPhoneUpdateContactResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneUpdateContactTool: ToolConfig<
AgentPhoneUpdateContactParams,
AgentPhoneUpdateContactResult
> = {
id: 'agentphone_update_contact',
name: 'Update Contact',
description: "Update a contact's fields",
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
contactId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Contact ID',
},
phoneNumber: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New phone number in E.164 format',
},
name: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New contact name',
},
email: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New email address',
},
notes: {
type: 'string',
required: false,
visibility: 'user-or-llm',
description: 'New freeform notes',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/contacts/${params.contactId.trim()}`,
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.phoneNumber) body.phoneNumber = params.phoneNumber
if (params.name) body.name = params.name
if (params.email) body.email = params.email
if (params.notes) body.notes = params.notes
return body
},
},
transformResponse: async (response): Promise<AgentPhoneUpdateContactResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to update contact',
output: {
id: '',
phoneNumber: '',
name: '',
email: null,
notes: null,
createdAt: '',
updatedAt: '',
},
}
}
return {
success: true,
output: {
id: data.id ?? '',
phoneNumber: data.phoneNumber ?? '',
name: data.name ?? '',
email: data.email ?? null,
notes: data.notes ?? null,
createdAt: data.createdAt ?? '',
updatedAt: data.updatedAt ?? '',
},
}
},
outputs: {
id: { type: 'string', description: 'Contact ID' },
phoneNumber: { type: 'string', description: 'Phone number in E.164 format' },
name: { type: 'string', description: 'Contact name' },
email: { type: 'string', description: 'Contact email address', optional: true },
notes: { type: 'string', description: 'Freeform notes', optional: true },
createdAt: { type: 'string', description: 'ISO 8601 creation timestamp' },
updatedAt: { type: 'string', description: 'ISO 8601 update timestamp' },
},
}
@@ -0,0 +1,152 @@
import type {
AgentPhoneConversationMessage,
AgentPhoneUpdateConversationParams,
AgentPhoneUpdateConversationResult,
} from '@/tools/agentphone/types'
import type { ToolConfig } from '@/tools/types'
export const agentphoneUpdateConversationTool: ToolConfig<
AgentPhoneUpdateConversationParams,
AgentPhoneUpdateConversationResult
> = {
id: 'agentphone_update_conversation',
name: 'Update Conversation',
description: 'Update conversation metadata (stored state). Pass null to clear existing metadata.',
version: '1.0.0',
params: {
apiKey: {
type: 'string',
required: true,
visibility: 'user-only',
description: 'AgentPhone API key',
},
conversationId: {
type: 'string',
required: true,
visibility: 'user-or-llm',
description: 'Conversation ID',
},
metadata: {
type: 'json',
required: false,
visibility: 'user-or-llm',
description:
'Custom key-value metadata to store on the conversation. Pass null to clear existing metadata.',
},
},
request: {
url: (params) => `https://api.agentphone.to/v1/conversations/${params.conversationId.trim()}`,
method: 'PATCH',
headers: (params) => ({
Authorization: `Bearer ${params.apiKey}`,
'Content-Type': 'application/json',
}),
body: (params) => {
const body: Record<string, unknown> = {}
if (params.metadata !== undefined) body.metadata = params.metadata
return body
},
},
transformResponse: async (response): Promise<AgentPhoneUpdateConversationResult> => {
const data = await response.json()
if (!response.ok) {
return {
success: false,
error: data?.detail?.[0]?.msg ?? data?.message ?? 'Failed to update conversation',
output: {
id: '',
agentId: null,
phoneNumberId: '',
phoneNumber: '',
participant: '',
lastMessageAt: '',
messageCount: 0,
metadata: null,
createdAt: '',
messages: [],
},
}
}
const rawMessages = Array.isArray(data?.messages) ? data.messages : []
const messages: AgentPhoneConversationMessage[] = rawMessages.map(
(message: Record<string, unknown>) => ({
id: (message.id as string) ?? '',
body: (message.body as string) ?? '',
fromNumber: (message.fromNumber as string) ?? '',
toNumber: (message.toNumber as string) ?? '',
direction: (message.direction as string) ?? '',
channel: (message.channel as string | null) ?? null,
mediaUrl: (message.mediaUrl as string | null) ?? null,
mediaUrls: Array.isArray(message.mediaUrls) ? (message.mediaUrls as string[]) : [],
receivedAt: (message.receivedAt as string) ?? '',
})
)
return {
success: true,
output: {
id: data.id ?? '',
agentId: data.agentId ?? null,
phoneNumberId: data.phoneNumberId ?? '',
phoneNumber: data.phoneNumber ?? '',
participant: data.participant ?? '',
lastMessageAt: data.lastMessageAt ?? '',
messageCount: data.messageCount ?? 0,
metadata: data.metadata ?? null,
createdAt: data.createdAt ?? '',
messages,
},
}
},
outputs: {
id: { type: 'string', description: 'Conversation ID' },
agentId: { type: 'string', description: 'Agent ID', optional: true },
phoneNumberId: { type: 'string', description: 'Phone number ID' },
phoneNumber: { type: 'string', description: 'Phone number' },
participant: { type: 'string', description: 'External participant phone number' },
lastMessageAt: { type: 'string', description: 'ISO 8601 timestamp' },
messageCount: { type: 'number', description: 'Number of messages' },
metadata: {
type: 'json',
description: 'Custom metadata stored on the conversation',
optional: true,
},
createdAt: { type: 'string', description: 'ISO 8601 timestamp' },
messages: {
type: 'array',
description: 'Messages in the conversation',
items: {
type: 'object',
properties: {
id: { type: 'string', description: 'Message ID' },
body: { type: 'string', description: 'Message body' },
fromNumber: { type: 'string', description: 'Sender phone number' },
toNumber: { type: 'string', description: 'Recipient phone number' },
direction: { type: 'string', description: 'inbound or outbound' },
channel: {
type: 'string',
description: 'Channel (sms, mms, etc.)',
optional: true,
},
mediaUrl: {
type: 'string',
description: 'Media URL if any',
optional: true,
},
mediaUrls: {
type: 'array',
description: 'All attached media URLs',
items: { type: 'string' },
},
receivedAt: { type: 'string', description: 'ISO 8601 timestamp' },
},
},
},
},
}