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
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:
@@ -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 },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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'
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Reference in New Issue
Block a user