@tangle-network/agent-integrations 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-APQQVVWS.js → chunk-O4DODWEG.js} +3 -3
- package/dist/{chunk-ZD7FKHF5.js → chunk-RFJSN3YW.js} +598 -617
- package/dist/{chunk-ZD7FKHF5.js.map → chunk-RFJSN3YW.js.map} +1 -1
- package/dist/{chunk-JC3HRBWU.js → chunk-TOYD2MCA.js} +2 -2
- package/dist/connectors/adapters/index.d.ts +8 -1
- package/dist/connectors/adapters/index.js +3 -1
- package/dist/connectors/index.d.ts +1 -1
- package/dist/connectors/index.js +4 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -3
- package/dist/internal/bundled-manifest-runtime.js +2 -2
- package/dist/mcp.js +3 -3
- package/dist/registry.js +3 -3
- package/docs/one-ph0ny-client.md +99 -0
- package/package.json +2 -1
- /package/dist/{chunk-APQQVVWS.js.map → chunk-O4DODWEG.js.map} +0 -0
- /package/dist/{chunk-JC3HRBWU.js.map → chunk-TOYD2MCA.js.map} +0 -0
|
@@ -203,6 +203,7 @@ __export(adapters_exports, {
|
|
|
203
203
|
createLinkedinConnector: () => createLinkedinConnector,
|
|
204
204
|
createMongoDbConnector: () => createMongoDbConnector,
|
|
205
205
|
createMySqlConnector: () => createMySqlConnector,
|
|
206
|
+
createPhonyConnector: () => createPhonyConnector,
|
|
206
207
|
createPostgresConnector: () => createPostgresConnector,
|
|
207
208
|
createRabbitMqConnector: () => createRabbitMqConnector,
|
|
208
209
|
createRedisConnector: () => createRedisConnector,
|
|
@@ -703,6 +704,575 @@ __export(adapters_exports, {
|
|
|
703
704
|
zuoraConnector: () => zuoraConnector
|
|
704
705
|
});
|
|
705
706
|
|
|
707
|
+
// src/connectors/adapters/phony.ts
|
|
708
|
+
import { VoiceClient, VoiceApiException } from "@ph0ny/sdk";
|
|
709
|
+
var E164 = /^\+[1-9]\d{7,14}$/;
|
|
710
|
+
function createPhonyConnector(options = {}) {
|
|
711
|
+
async function ph0ny(sourceId, token, request4, label) {
|
|
712
|
+
try {
|
|
713
|
+
return await new VoiceClient({ apiKey: token, baseUrl: options.baseUrl, fetchImpl: options.fetchImpl }).request(request4.method, request4.path, {
|
|
714
|
+
body: request4.body,
|
|
715
|
+
timeout: request4.timeout
|
|
716
|
+
});
|
|
717
|
+
} catch (error) {
|
|
718
|
+
if (!(error instanceof VoiceApiException)) throw error;
|
|
719
|
+
if (error.statusCode === 401) throw new CredentialsExpired("ph0ny rejected credentials (401)", sourceId);
|
|
720
|
+
throw new Error(`phony ${label} ${error.statusCode}: ${error.message.slice(0, 200)}`);
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
return {
|
|
724
|
+
manifest: {
|
|
725
|
+
kind: "phony",
|
|
726
|
+
displayName: "ph0ny",
|
|
727
|
+
description: "Place real outbound phone calls with a voice agent, then read back call status, transcript, and extracted fields. Outbound calls require recorded user consent and support a dry-run that validates the full configuration without dialing.",
|
|
728
|
+
auth: {
|
|
729
|
+
kind: "api-key",
|
|
730
|
+
hint: "Paste your ph0ny API key (plabs_\u2026). Create one in the developer portal via POST /v1/keys \u2014 it is shown once."
|
|
731
|
+
},
|
|
732
|
+
category: "comms",
|
|
733
|
+
// A call's status/transcript evolve while it is live, so a fetched row
|
|
734
|
+
// can be stale moments later — reads are point-in-time, not authoritative
|
|
735
|
+
// truth. start_outbound_call creates a fresh, uncontended call each time
|
|
736
|
+
// (cas='none', fire-and-forget external effect; no upstream CAS exists).
|
|
737
|
+
defaultConsistencyModel: "cache",
|
|
738
|
+
capabilities: [
|
|
739
|
+
{
|
|
740
|
+
name: "list_agents",
|
|
741
|
+
class: "read",
|
|
742
|
+
description: "List the voice agents on your ph0ny developer account (newest first).",
|
|
743
|
+
parameters: {
|
|
744
|
+
type: "object",
|
|
745
|
+
properties: {
|
|
746
|
+
limit: { type: "integer", minimum: 1, maximum: 100, default: 20 },
|
|
747
|
+
cursor: { type: "string", description: "Pagination cursor from a prior response." }
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
{
|
|
752
|
+
name: "get_call",
|
|
753
|
+
class: "read",
|
|
754
|
+
description: "Fetch a single outbound call by id, including status, transcript, and extracted fields.",
|
|
755
|
+
parameters: {
|
|
756
|
+
type: "object",
|
|
757
|
+
properties: {
|
|
758
|
+
id: { type: "string", description: "Outbound call id returned by start_outbound_call." }
|
|
759
|
+
},
|
|
760
|
+
required: ["id"]
|
|
761
|
+
}
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
name: "list_calls",
|
|
765
|
+
class: "read",
|
|
766
|
+
description: "List recent outbound calls for the account, optionally filtered by agent.",
|
|
767
|
+
parameters: {
|
|
768
|
+
type: "object",
|
|
769
|
+
properties: {
|
|
770
|
+
agentId: { type: "string", description: "Optional filter \u2014 only calls placed by this agent." },
|
|
771
|
+
limit: { type: "integer", minimum: 1, maximum: 50, default: 20 }
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
},
|
|
775
|
+
{
|
|
776
|
+
name: "start_outbound_call",
|
|
777
|
+
class: "mutation",
|
|
778
|
+
description: "Place an outbound phone call. Requires userConsentRecorded=true (the user must have explicitly authorized the call). Set dryRun=true to validate the full configuration without dialing.",
|
|
779
|
+
cas: "none",
|
|
780
|
+
externalEffect: true,
|
|
781
|
+
parameters: {
|
|
782
|
+
type: "object",
|
|
783
|
+
properties: {
|
|
784
|
+
agentId: { type: "string", description: "Agent that will place the call (agentKind must be personal_assistant)." },
|
|
785
|
+
toNumber: { type: "string", description: "E.164 destination, e.g. +14155551212." },
|
|
786
|
+
fromNumber: { type: "string", description: "E.164 caller number provisioned to your developer account." },
|
|
787
|
+
mission: {
|
|
788
|
+
type: "object",
|
|
789
|
+
description: "What the agent should accomplish on the call.",
|
|
790
|
+
properties: {
|
|
791
|
+
goal: { type: "string", description: "Plain-language objective (8\u20132000 chars)." },
|
|
792
|
+
successSchema: { type: "object", description: "Optional JSON schema describing fields to extract on success." },
|
|
793
|
+
ivrHints: {
|
|
794
|
+
type: "array",
|
|
795
|
+
items: { type: "string" },
|
|
796
|
+
description: "Optional hints for navigating phone-tree / IVR menus (\u22648 entries)."
|
|
797
|
+
},
|
|
798
|
+
maxTurns: { type: "integer", minimum: 1, maximum: 60 },
|
|
799
|
+
maxDurationMs: { type: "integer", minimum: 3e4, maximum: 12e5 }
|
|
800
|
+
},
|
|
801
|
+
required: ["goal"]
|
|
802
|
+
},
|
|
803
|
+
missionId: { type: "string", description: "Optional caller-supplied mission id." },
|
|
804
|
+
callerProfile: {
|
|
805
|
+
type: "object",
|
|
806
|
+
properties: {
|
|
807
|
+
userName: { type: "string" },
|
|
808
|
+
companyName: { type: "string" }
|
|
809
|
+
}
|
|
810
|
+
},
|
|
811
|
+
voiceCloneId: { type: "string", description: "Optional cloned-voice id to speak with." },
|
|
812
|
+
userConsentRecorded: {
|
|
813
|
+
type: "boolean",
|
|
814
|
+
description: "REQUIRED. Must be true \u2014 the user explicitly authorized this call. ph0ny rejects false."
|
|
815
|
+
},
|
|
816
|
+
dryRun: {
|
|
817
|
+
type: "boolean",
|
|
818
|
+
description: "When true, validate every gate and return a dryRunReport without placing the call."
|
|
819
|
+
}
|
|
820
|
+
},
|
|
821
|
+
required: ["agentId", "toNumber", "fromNumber", "mission", "userConsentRecorded"]
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
{
|
|
825
|
+
name: "create_agent",
|
|
826
|
+
class: "mutation",
|
|
827
|
+
description: "Create a voice agent on your ph0ny developer account. Only name is required; the rest configure the LLM, TTS, voice, knowledge base, and capture behavior.",
|
|
828
|
+
cas: "none",
|
|
829
|
+
externalEffect: true,
|
|
830
|
+
parameters: {
|
|
831
|
+
type: "object",
|
|
832
|
+
properties: {
|
|
833
|
+
id: { type: "string", description: "Optional caller-supplied agent id (1\u201360 chars)." },
|
|
834
|
+
name: { type: "string", description: "Display name (1\u2013100 chars)." },
|
|
835
|
+
description: { type: "string", description: "Optional description (\u22641000 chars)." },
|
|
836
|
+
systemPrompt: { type: "string", description: "Optional system prompt (\u226410000 chars)." },
|
|
837
|
+
firstMessage: { type: "string", description: "Optional opening line the agent speaks first (\u22641000 chars)." },
|
|
838
|
+
voiceId: { type: "string", description: "Optional TTS voice id." },
|
|
839
|
+
ttsProvider: { type: "string", description: 'TTS provider; "default" lets ph0ny choose.' },
|
|
840
|
+
ttsModel: { type: "string", description: "Optional TTS model id." },
|
|
841
|
+
sttProvider: { type: "string", description: "Optional speech-to-text provider." },
|
|
842
|
+
llmProvider: { type: "string", description: "LLM provider (e.g. openai, anthropic). Defaults to openai server-side." },
|
|
843
|
+
llmModel: { type: "string", description: "Optional LLM model id." },
|
|
844
|
+
libraryId: { type: "string", description: "Ingest-service library id (preferred over collectionId)." },
|
|
845
|
+
collectionId: { type: "string", description: "Deprecated \u2014 KB collection id to attach. Prefer libraryId." },
|
|
846
|
+
styleProfile: { type: "object", description: "Optional style profile object." },
|
|
847
|
+
promptByModel: { type: "object", description: "Optional per-model prompt overrides (modelId \u2192 prompt)." },
|
|
848
|
+
language: { type: "string", description: 'BCP-47 language code; defaults to "en".' },
|
|
849
|
+
temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature (0\u20132); defaults to 0.7." },
|
|
850
|
+
maxTokens: { type: "integer", minimum: 1, maximum: 16384, description: "Optional max output tokens." },
|
|
851
|
+
contactCaptureEnabled: { type: "boolean", description: "Enable structured contact capture during calls." },
|
|
852
|
+
contactCaptureFields: {
|
|
853
|
+
type: "array",
|
|
854
|
+
items: { type: "string", enum: ["name", "email", "phone"] },
|
|
855
|
+
description: "Which contact fields to capture when capture is enabled."
|
|
856
|
+
},
|
|
857
|
+
metadata: { type: "object", description: "Arbitrary metadata object stored with the agent." }
|
|
858
|
+
},
|
|
859
|
+
required: ["name"]
|
|
860
|
+
}
|
|
861
|
+
},
|
|
862
|
+
{
|
|
863
|
+
name: "provision_agent",
|
|
864
|
+
class: "mutation",
|
|
865
|
+
description: "Create an agent and, in the same atomic call, optionally a fresh knowledge-base collection and seed it with initial content. Returns the agent plus the created collection and ingestion results.",
|
|
866
|
+
cas: "none",
|
|
867
|
+
externalEffect: true,
|
|
868
|
+
parameters: {
|
|
869
|
+
type: "object",
|
|
870
|
+
properties: {
|
|
871
|
+
id: { type: "string", description: "Optional caller-supplied agent id (1\u201360 chars)." },
|
|
872
|
+
name: { type: "string", description: "Display name (1\u2013100 chars)." },
|
|
873
|
+
description: { type: "string", description: "Optional description (\u22641000 chars)." },
|
|
874
|
+
systemPrompt: { type: "string", description: "Optional system prompt (\u226410000 chars)." },
|
|
875
|
+
firstMessage: { type: "string", description: "Optional opening line the agent speaks first (\u22641000 chars)." },
|
|
876
|
+
voiceId: { type: "string", description: "Optional TTS voice id." },
|
|
877
|
+
ttsProvider: { type: "string", description: 'TTS provider; "default" lets ph0ny choose.' },
|
|
878
|
+
ttsModel: { type: "string", description: "Optional TTS model id." },
|
|
879
|
+
sttProvider: { type: "string", description: "Optional speech-to-text provider." },
|
|
880
|
+
llmProvider: { type: "string", description: "LLM provider (e.g. openai, anthropic). Defaults to openai server-side." },
|
|
881
|
+
llmModel: { type: "string", description: "Optional LLM model id." },
|
|
882
|
+
libraryId: { type: "string", description: "Ingest-service library id (preferred over collectionId)." },
|
|
883
|
+
collectionId: { type: "string", description: "Existing KB collection id to attach instead of creating one." },
|
|
884
|
+
styleProfile: { type: "object", description: "Optional style profile object." },
|
|
885
|
+
promptByModel: { type: "object", description: "Optional per-model prompt overrides (modelId \u2192 prompt)." },
|
|
886
|
+
language: { type: "string", description: 'BCP-47 language code; defaults to "en".' },
|
|
887
|
+
temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature (0\u20132); defaults to 0.7." },
|
|
888
|
+
maxTokens: { type: "integer", minimum: 1, maximum: 16384, description: "Optional max output tokens." },
|
|
889
|
+
contactCaptureEnabled: { type: "boolean", description: "Enable structured contact capture during calls." },
|
|
890
|
+
contactCaptureFields: {
|
|
891
|
+
type: "array",
|
|
892
|
+
items: { type: "string", enum: ["name", "email", "phone"] },
|
|
893
|
+
description: "Which contact fields to capture when capture is enabled."
|
|
894
|
+
},
|
|
895
|
+
metadata: { type: "object", description: "Arbitrary metadata object stored with the agent." },
|
|
896
|
+
collection: {
|
|
897
|
+
type: "object",
|
|
898
|
+
description: "When present, create a fresh KB collection and attach it to the agent.",
|
|
899
|
+
properties: {
|
|
900
|
+
name: { type: "string", description: "Collection name (1\u2013100 chars)." },
|
|
901
|
+
description: { type: "string", description: "Optional collection description (\u2264500 chars)." }
|
|
902
|
+
},
|
|
903
|
+
required: ["name"]
|
|
904
|
+
},
|
|
905
|
+
initialContent: {
|
|
906
|
+
type: "array",
|
|
907
|
+
maxItems: 10,
|
|
908
|
+
description: "Documents to ingest into the new/attached collection (\u226410).",
|
|
909
|
+
items: {
|
|
910
|
+
type: "object",
|
|
911
|
+
properties: {
|
|
912
|
+
content: { type: "string", description: "Raw text or transcript content." },
|
|
913
|
+
contentType: { type: "string", enum: ["text", "transcript"], description: 'Defaults to "text".' },
|
|
914
|
+
metadata: { type: "object", description: "Arbitrary metadata stored with the document." }
|
|
915
|
+
},
|
|
916
|
+
required: ["content"]
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
},
|
|
920
|
+
required: ["name"]
|
|
921
|
+
}
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
name: "kb_create_collection",
|
|
925
|
+
class: "mutation",
|
|
926
|
+
description: "Create a knowledge-base collection the agent can search at call time.",
|
|
927
|
+
cas: "none",
|
|
928
|
+
externalEffect: true,
|
|
929
|
+
parameters: {
|
|
930
|
+
type: "object",
|
|
931
|
+
properties: {
|
|
932
|
+
name: { type: "string", description: "Collection name (1\u2013100 chars)." },
|
|
933
|
+
description: { type: "string", description: "Optional description (\u2264500 chars)." },
|
|
934
|
+
metadata: { type: "object", description: "Arbitrary metadata object stored with the collection." }
|
|
935
|
+
},
|
|
936
|
+
required: ["name"]
|
|
937
|
+
}
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
name: "kb_ingest",
|
|
941
|
+
class: "mutation",
|
|
942
|
+
description: 'Chunk, embed, and store content into a KB collection. Provide content directly (\u22641MB), or a sourceUrl for audio transcription. contentType "url" is not yet implemented server-side (501).',
|
|
943
|
+
cas: "none",
|
|
944
|
+
externalEffect: true,
|
|
945
|
+
parameters: {
|
|
946
|
+
type: "object",
|
|
947
|
+
properties: {
|
|
948
|
+
collectionId: { type: "string", description: "Target collection id." },
|
|
949
|
+
content: { type: "string", description: "Raw text/transcript content (\u22641,000,000 chars)." },
|
|
950
|
+
sourceUrl: { type: "string", description: 'Public URL \u2014 required when contentType is "audio" (transcribed) or "url".' },
|
|
951
|
+
contentType: {
|
|
952
|
+
type: "string",
|
|
953
|
+
enum: ["text", "transcript", "audio", "url"],
|
|
954
|
+
description: 'Defaults to "text". "audio" transcribes sourceUrl; "url" returns 501.'
|
|
955
|
+
},
|
|
956
|
+
metadata: { type: "object", description: "Arbitrary metadata stored with the document." },
|
|
957
|
+
chunkSize: { type: "integer", minimum: 100, maximum: 4e3, description: "Chunk size in chars; defaults to 1000." },
|
|
958
|
+
chunkOverlap: { type: "integer", minimum: 0, maximum: 500, description: "Chunk overlap in chars; defaults to 200. Must be < chunkSize." }
|
|
959
|
+
},
|
|
960
|
+
required: ["collectionId"]
|
|
961
|
+
}
|
|
962
|
+
},
|
|
963
|
+
{
|
|
964
|
+
name: "kb_search",
|
|
965
|
+
class: "read",
|
|
966
|
+
description: "Hybrid vector + keyword search over a KB collection. Returns ranked results with scores.",
|
|
967
|
+
parameters: {
|
|
968
|
+
type: "object",
|
|
969
|
+
properties: {
|
|
970
|
+
collectionId: { type: "string", description: "Collection id to search." },
|
|
971
|
+
query: { type: "string", description: "Search query (1\u20132000 chars)." },
|
|
972
|
+
limit: { type: "integer", minimum: 1, maximum: 100, description: "Max results; defaults to 10." },
|
|
973
|
+
threshold: { type: "number", minimum: 0, maximum: 1, description: "Minimum similarity score; defaults to 0.7." },
|
|
974
|
+
includeMetadata: { type: "boolean", description: "Include per-result metadata; defaults to true." }
|
|
975
|
+
},
|
|
976
|
+
required: ["collectionId", "query"]
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
]
|
|
980
|
+
},
|
|
981
|
+
async executeRead(inv) {
|
|
982
|
+
const token = bearerToken(inv.source.credentials);
|
|
983
|
+
if (inv.capabilityName === "list_agents") {
|
|
984
|
+
const { limit, cursor } = inv.args;
|
|
985
|
+
const params = new URLSearchParams();
|
|
986
|
+
params.set("limit", String(Math.min(Math.max(1, limit ?? 20), 100)));
|
|
987
|
+
if (cursor) params.set("cursor", cursor);
|
|
988
|
+
const json = await ph0ny(
|
|
989
|
+
inv.source.id,
|
|
990
|
+
token,
|
|
991
|
+
{ method: "GET", path: `/v1/agents?${params.toString()}`, timeout: 1e4 },
|
|
992
|
+
"list_agents"
|
|
993
|
+
);
|
|
994
|
+
return {
|
|
995
|
+
data: { agents: json.data ?? [], nextCursor: json.nextCursor ?? null, hasMore: json.hasMore ?? false },
|
|
996
|
+
fetchedAt: Date.now()
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
if (inv.capabilityName === "get_call") {
|
|
1000
|
+
const { id: id7 } = inv.args;
|
|
1001
|
+
const json = await ph0ny(
|
|
1002
|
+
inv.source.id,
|
|
1003
|
+
token,
|
|
1004
|
+
{ method: "GET", path: `/v1/outbound/${encodeURIComponent(id7)}`, timeout: 1e4 },
|
|
1005
|
+
"get_call"
|
|
1006
|
+
);
|
|
1007
|
+
return { data: { call: json.call ?? null }, fetchedAt: Date.now() };
|
|
1008
|
+
}
|
|
1009
|
+
if (inv.capabilityName === "list_calls") {
|
|
1010
|
+
const { agentId, limit } = inv.args;
|
|
1011
|
+
const params = new URLSearchParams();
|
|
1012
|
+
params.set("limit", String(Math.min(Math.max(1, limit ?? 20), 50)));
|
|
1013
|
+
if (agentId) params.set("agentId", agentId);
|
|
1014
|
+
const json = await ph0ny(
|
|
1015
|
+
inv.source.id,
|
|
1016
|
+
token,
|
|
1017
|
+
{ method: "GET", path: `/v1/outbound?${params.toString()}`, timeout: 1e4 },
|
|
1018
|
+
"list_calls"
|
|
1019
|
+
);
|
|
1020
|
+
return { data: { calls: json.calls ?? [] }, fetchedAt: Date.now() };
|
|
1021
|
+
}
|
|
1022
|
+
if (inv.capabilityName === "kb_search") {
|
|
1023
|
+
const { collectionId, query: query2, limit, threshold, includeMetadata } = inv.args;
|
|
1024
|
+
const payload = { query: query2 };
|
|
1025
|
+
if (limit !== void 0) payload.limit = limit;
|
|
1026
|
+
if (threshold !== void 0) payload.threshold = threshold;
|
|
1027
|
+
if (includeMetadata !== void 0) payload.includeMetadata = includeMetadata;
|
|
1028
|
+
const json = await ph0ny(
|
|
1029
|
+
inv.source.id,
|
|
1030
|
+
token,
|
|
1031
|
+
{
|
|
1032
|
+
method: "POST",
|
|
1033
|
+
path: `/v1/collections/${encodeURIComponent(collectionId)}/search`,
|
|
1034
|
+
body: payload,
|
|
1035
|
+
timeout: 15e3
|
|
1036
|
+
},
|
|
1037
|
+
"kb_search"
|
|
1038
|
+
);
|
|
1039
|
+
return {
|
|
1040
|
+
data: {
|
|
1041
|
+
results: json.results ?? [],
|
|
1042
|
+
queryTokens: json.queryTokens ?? 0,
|
|
1043
|
+
...json.graphContext !== void 0 ? { graphContext: json.graphContext } : {}
|
|
1044
|
+
},
|
|
1045
|
+
fetchedAt: Date.now()
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
throw new Error(`phony: unknown read capability ${inv.capabilityName}`);
|
|
1049
|
+
},
|
|
1050
|
+
async executeMutation(inv) {
|
|
1051
|
+
const token = bearerToken(inv.source.credentials);
|
|
1052
|
+
if (inv.capabilityName === "start_outbound_call") {
|
|
1053
|
+
const args = validateOutboundStartArgs(inv.args);
|
|
1054
|
+
const payload = {
|
|
1055
|
+
agentId: args.agentId,
|
|
1056
|
+
toNumber: args.toNumber,
|
|
1057
|
+
fromNumber: args.fromNumber,
|
|
1058
|
+
mission: args.mission,
|
|
1059
|
+
userConsentRecorded: args.userConsentRecorded
|
|
1060
|
+
};
|
|
1061
|
+
if (args.missionId !== void 0) payload.missionId = args.missionId;
|
|
1062
|
+
if (args.callerProfile !== void 0) payload.callerProfile = args.callerProfile;
|
|
1063
|
+
if (args.voiceCloneId !== void 0) payload.voiceCloneId = args.voiceCloneId;
|
|
1064
|
+
if (args.dryRun !== void 0) payload.dryRun = args.dryRun;
|
|
1065
|
+
const json = await ph0ny(inv.source.id, token, { method: "POST", path: "/v1/outbound/start", body: payload, timeout: 2e4 }, "start_outbound_call");
|
|
1066
|
+
return {
|
|
1067
|
+
status: "committed",
|
|
1068
|
+
data: {
|
|
1069
|
+
callId: json.callId,
|
|
1070
|
+
callSid: json.callSid,
|
|
1071
|
+
callStatus: json.status,
|
|
1072
|
+
dryRun: json.dryRun ?? false,
|
|
1073
|
+
...json.dryRunReport !== void 0 ? { dryRunReport: json.dryRunReport } : {}
|
|
1074
|
+
},
|
|
1075
|
+
committedAt: Date.now(),
|
|
1076
|
+
idempotentReplay: false
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
if (inv.capabilityName === "create_agent") {
|
|
1080
|
+
const payload = pick(inv.args, AGENT_FIELDS);
|
|
1081
|
+
const json = await ph0ny(
|
|
1082
|
+
inv.source.id,
|
|
1083
|
+
token,
|
|
1084
|
+
{ method: "POST", path: `/v1/agents`, body: payload, timeout: 2e4 },
|
|
1085
|
+
"create_agent"
|
|
1086
|
+
);
|
|
1087
|
+
return {
|
|
1088
|
+
status: "committed",
|
|
1089
|
+
data: { agent: json },
|
|
1090
|
+
committedAt: Date.now(),
|
|
1091
|
+
idempotentReplay: false
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
if (inv.capabilityName === "provision_agent") {
|
|
1095
|
+
const args = inv.args;
|
|
1096
|
+
const payload = pick(args, AGENT_FIELDS);
|
|
1097
|
+
if (args.collection !== void 0) payload.collection = args.collection;
|
|
1098
|
+
if (args.initialContent !== void 0) payload.initialContent = args.initialContent;
|
|
1099
|
+
const json = await ph0ny(
|
|
1100
|
+
inv.source.id,
|
|
1101
|
+
token,
|
|
1102
|
+
{ method: "POST", path: `/v1/agents/provision`, body: payload, timeout: 2e4 },
|
|
1103
|
+
"provision_agent"
|
|
1104
|
+
);
|
|
1105
|
+
return {
|
|
1106
|
+
status: "committed",
|
|
1107
|
+
data: {
|
|
1108
|
+
agent: json.agent ?? null,
|
|
1109
|
+
...json.collection !== void 0 ? { collection: json.collection } : {},
|
|
1110
|
+
...json.ingested !== void 0 ? { ingested: json.ingested } : {}
|
|
1111
|
+
},
|
|
1112
|
+
committedAt: Date.now(),
|
|
1113
|
+
idempotentReplay: false
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
if (inv.capabilityName === "kb_create_collection") {
|
|
1117
|
+
const { name, description, metadata } = inv.args;
|
|
1118
|
+
const payload = { name };
|
|
1119
|
+
if (description !== void 0) payload.description = description;
|
|
1120
|
+
if (metadata !== void 0) payload.metadata = metadata;
|
|
1121
|
+
const json = await ph0ny(
|
|
1122
|
+
inv.source.id,
|
|
1123
|
+
token,
|
|
1124
|
+
{ method: "POST", path: `/v1/collections`, body: payload, timeout: 2e4 },
|
|
1125
|
+
"kb_create_collection"
|
|
1126
|
+
);
|
|
1127
|
+
return {
|
|
1128
|
+
status: "committed",
|
|
1129
|
+
data: { collection: json },
|
|
1130
|
+
committedAt: Date.now(),
|
|
1131
|
+
idempotentReplay: false
|
|
1132
|
+
};
|
|
1133
|
+
}
|
|
1134
|
+
if (inv.capabilityName === "kb_ingest") {
|
|
1135
|
+
const { collectionId, ...rest } = inv.args;
|
|
1136
|
+
const payload = pick(rest, INGEST_FIELDS);
|
|
1137
|
+
const json = await ph0ny(
|
|
1138
|
+
inv.source.id,
|
|
1139
|
+
token,
|
|
1140
|
+
{ method: "POST", path: `/v1/collections/${encodeURIComponent(collectionId)}/ingest`, body: payload, timeout: 2e4 },
|
|
1141
|
+
"kb_ingest"
|
|
1142
|
+
);
|
|
1143
|
+
return {
|
|
1144
|
+
status: "committed",
|
|
1145
|
+
data: {
|
|
1146
|
+
documentId: json.documentId ?? null,
|
|
1147
|
+
chunksCreated: json.chunksCreated ?? 0,
|
|
1148
|
+
tokensUsed: json.tokensUsed ?? 0
|
|
1149
|
+
},
|
|
1150
|
+
committedAt: Date.now(),
|
|
1151
|
+
idempotentReplay: false
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
throw new Error(`phony: unknown mutation capability ${inv.capabilityName}`);
|
|
1155
|
+
},
|
|
1156
|
+
async test(source) {
|
|
1157
|
+
try {
|
|
1158
|
+
const token = bearerToken(source.credentials);
|
|
1159
|
+
await new VoiceClient({ apiKey: token, baseUrl: options.baseUrl, fetchImpl: options.fetchImpl }).request("GET", "/v1/outbound?limit=1", { timeout: 8e3 });
|
|
1160
|
+
return { ok: true };
|
|
1161
|
+
} catch (err) {
|
|
1162
|
+
if (err instanceof VoiceApiException) {
|
|
1163
|
+
return {
|
|
1164
|
+
ok: false,
|
|
1165
|
+
reason: err.statusCode === 401 ? "ph0ny rejected credentials (401) \u2014 reconnect required" : `ph0ny returned ${err.statusCode}`
|
|
1166
|
+
};
|
|
1167
|
+
}
|
|
1168
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
};
|
|
1172
|
+
}
|
|
1173
|
+
var phonyConnector = createPhonyConnector();
|
|
1174
|
+
function bearerToken(creds) {
|
|
1175
|
+
if (creds.kind !== "api-key" || typeof creds.apiKey !== "string" || creds.apiKey.length === 0) {
|
|
1176
|
+
throw new Error("phony: expected api-key credentials");
|
|
1177
|
+
}
|
|
1178
|
+
return creds.apiKey;
|
|
1179
|
+
}
|
|
1180
|
+
function validateOutboundStartArgs(args) {
|
|
1181
|
+
if (args.userConsentRecorded !== true) {
|
|
1182
|
+
throw new Error(
|
|
1183
|
+
"phony start_outbound_call requires userConsentRecorded=true after explicit user authorization; refusing before contacting ph0ny"
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
assertNonEmptyString(args.agentId, "agentId", 128);
|
|
1187
|
+
assertE164(args.toNumber, "toNumber");
|
|
1188
|
+
assertE164(args.fromNumber, "fromNumber");
|
|
1189
|
+
if (!isRecord(args.mission)) throw new Error("phony start_outbound_call requires mission");
|
|
1190
|
+
const mission = args.mission;
|
|
1191
|
+
assertNonEmptyString(mission.goal, "mission.goal", 2e3);
|
|
1192
|
+
if (mission.goal.trim().length < 8) throw new Error("phony start_outbound_call mission.goal must be at least 8 characters");
|
|
1193
|
+
if (mission.successSchema !== void 0 && !isRecord(mission.successSchema)) {
|
|
1194
|
+
throw new Error("phony start_outbound_call mission.successSchema must be an object when supplied");
|
|
1195
|
+
}
|
|
1196
|
+
if (mission.ivrHints !== void 0) {
|
|
1197
|
+
if (!Array.isArray(mission.ivrHints) || mission.ivrHints.length > 8) {
|
|
1198
|
+
throw new Error("phony start_outbound_call mission.ivrHints must be an array with at most 8 entries");
|
|
1199
|
+
}
|
|
1200
|
+
for (const hint of mission.ivrHints) assertNonEmptyString(hint, "mission.ivrHints[]", 200);
|
|
1201
|
+
}
|
|
1202
|
+
assertOptionalInteger(mission.maxTurns, "mission.maxTurns", 1, 60);
|
|
1203
|
+
assertOptionalInteger(mission.maxDurationMs, "mission.maxDurationMs", 3e4, 20 * 60 * 1e3);
|
|
1204
|
+
if (args.missionId !== void 0) assertNonEmptyString(args.missionId, "missionId", 128);
|
|
1205
|
+
if (args.voiceCloneId !== void 0) assertNonEmptyString(args.voiceCloneId, "voiceCloneId", 128);
|
|
1206
|
+
if (args.callerProfile !== void 0) {
|
|
1207
|
+
if (!isRecord(args.callerProfile)) throw new Error("phony start_outbound_call callerProfile must be an object");
|
|
1208
|
+
if (args.callerProfile.userName !== void 0) assertNonEmptyString(args.callerProfile.userName, "callerProfile.userName", 120);
|
|
1209
|
+
if (args.callerProfile.companyName !== void 0) {
|
|
1210
|
+
assertNonEmptyString(args.callerProfile.companyName, "callerProfile.companyName", 120);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
if (args.dryRun !== void 0 && typeof args.dryRun !== "boolean") {
|
|
1214
|
+
throw new Error("phony start_outbound_call dryRun must be boolean when supplied");
|
|
1215
|
+
}
|
|
1216
|
+
return args;
|
|
1217
|
+
}
|
|
1218
|
+
function assertNonEmptyString(value, field, maxLength) {
|
|
1219
|
+
if (typeof value !== "string" || value.trim().length === 0 || value.length > maxLength) {
|
|
1220
|
+
throw new Error(`phony start_outbound_call ${field} must be a non-empty string <= ${maxLength} chars`);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
function assertE164(value, field) {
|
|
1224
|
+
if (typeof value !== "string" || !E164.test(value)) {
|
|
1225
|
+
throw new Error(`phony start_outbound_call ${field} must be E.164 format, e.g. +14155550123`);
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
function assertOptionalInteger(value, field, min, max) {
|
|
1229
|
+
if (value === void 0) return;
|
|
1230
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
|
|
1231
|
+
throw new Error(`phony start_outbound_call ${field} must be an integer between ${min} and ${max}`);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
function isRecord(value) {
|
|
1235
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1236
|
+
}
|
|
1237
|
+
function pick(args, fields) {
|
|
1238
|
+
const out = {};
|
|
1239
|
+
for (const key of fields) {
|
|
1240
|
+
if (args[key] !== void 0) out[key] = args[key];
|
|
1241
|
+
}
|
|
1242
|
+
return out;
|
|
1243
|
+
}
|
|
1244
|
+
var AGENT_FIELDS = [
|
|
1245
|
+
"id",
|
|
1246
|
+
"name",
|
|
1247
|
+
"description",
|
|
1248
|
+
"systemPrompt",
|
|
1249
|
+
"firstMessage",
|
|
1250
|
+
"voiceId",
|
|
1251
|
+
"ttsProvider",
|
|
1252
|
+
"ttsModel",
|
|
1253
|
+
"sttProvider",
|
|
1254
|
+
"llmProvider",
|
|
1255
|
+
"llmModel",
|
|
1256
|
+
"libraryId",
|
|
1257
|
+
"collectionId",
|
|
1258
|
+
"styleProfile",
|
|
1259
|
+
"promptByModel",
|
|
1260
|
+
"language",
|
|
1261
|
+
"temperature",
|
|
1262
|
+
"maxTokens",
|
|
1263
|
+
"contactCaptureEnabled",
|
|
1264
|
+
"contactCaptureFields",
|
|
1265
|
+
"metadata"
|
|
1266
|
+
];
|
|
1267
|
+
var INGEST_FIELDS = [
|
|
1268
|
+
"content",
|
|
1269
|
+
"sourceUrl",
|
|
1270
|
+
"contentType",
|
|
1271
|
+
"metadata",
|
|
1272
|
+
"chunkSize",
|
|
1273
|
+
"chunkOverlap"
|
|
1274
|
+
];
|
|
1275
|
+
|
|
706
1276
|
// src/connectors/adapters/declarative-rest.ts
|
|
707
1277
|
import { isIP } from "net";
|
|
708
1278
|
|
|
@@ -8956,7 +9526,7 @@ var modelslabConnector = {
|
|
|
8956
9526
|
}
|
|
8957
9527
|
const prompt = requiredString2(inv.args, "prompt");
|
|
8958
9528
|
const data4 = await request(inv.source, `${baseUrl}/text2img`, {
|
|
8959
|
-
...
|
|
9529
|
+
...pick2(inv.args, [
|
|
8960
9530
|
"negative_prompt",
|
|
8961
9531
|
"model_id",
|
|
8962
9532
|
"width",
|
|
@@ -9027,7 +9597,7 @@ function requiredString2(args, key) {
|
|
|
9027
9597
|
if (typeof value !== "string" || !value) throw new Error(`modelslab: ${key} is required`);
|
|
9028
9598
|
return value;
|
|
9029
9599
|
}
|
|
9030
|
-
function
|
|
9600
|
+
function pick2(args, keys) {
|
|
9031
9601
|
return Object.fromEntries(keys.filter((key) => args[key] !== void 0).map((key) => [key, args[key]]));
|
|
9032
9602
|
}
|
|
9033
9603
|
async function readBody(response) {
|
|
@@ -25216,7 +25786,7 @@ var azureEventGridConnector = {
|
|
|
25216
25786
|
const entries = Array.isArray(parsed) ? parsed : [parsed];
|
|
25217
25787
|
if (entries.length > MAX_EVENTS) throw new Error(`Azure Event Grid delivery exceeds ${MAX_EVENTS} events`);
|
|
25218
25788
|
const validation = entries.find((entry) => isValidationEvent(entry));
|
|
25219
|
-
if (validation &&
|
|
25789
|
+
if (validation && isRecord2(validation.data) && typeof validation.data.validationCode === "string") {
|
|
25220
25790
|
return {
|
|
25221
25791
|
events: [],
|
|
25222
25792
|
response: { status: 200, body: { validationResponse: validation.data.validationCode } }
|
|
@@ -25266,7 +25836,7 @@ function parseCredentials(credentials, dataSourceId) {
|
|
|
25266
25836
|
} catch {
|
|
25267
25837
|
throw new CredentialsExpired("Azure Event Grid credentials must be valid JSON", dataSourceId);
|
|
25268
25838
|
}
|
|
25269
|
-
if (!
|
|
25839
|
+
if (!isRecord2(parsed)) throw new CredentialsExpired("Azure Event Grid credentials must be a JSON object", dataSourceId);
|
|
25270
25840
|
const endpointValue = credentialString(parsed.endpoint, "endpoint", dataSourceId);
|
|
25271
25841
|
const accessKey = credentialString(parsed.accessKey, "accessKey", dataSourceId);
|
|
25272
25842
|
const deliverySecret = credentialString(parsed.deliverySecret, "deliverySecret", dataSourceId);
|
|
@@ -25331,12 +25901,12 @@ function eventArray(value) {
|
|
|
25331
25901
|
throw new Error(`events must contain 1 to ${MAX_EVENTS} entries`);
|
|
25332
25902
|
}
|
|
25333
25903
|
return value.map((entry, index) => {
|
|
25334
|
-
if (!
|
|
25904
|
+
if (!isRecord2(entry)) throw new Error(`events[${index}] must be an object`);
|
|
25335
25905
|
return entry;
|
|
25336
25906
|
});
|
|
25337
25907
|
}
|
|
25338
25908
|
function inboundEvent(entry, index) {
|
|
25339
|
-
if (!
|
|
25909
|
+
if (!isRecord2(entry)) throw new Error(`Azure Event Grid delivery entry ${index} must be an object`);
|
|
25340
25910
|
const eventType = typeof entry.eventType === "string" ? entry.eventType : typeof entry.type === "string" ? entry.type : "azure.event-grid.event";
|
|
25341
25911
|
return {
|
|
25342
25912
|
eventType,
|
|
@@ -25345,7 +25915,7 @@ function inboundEvent(entry, index) {
|
|
|
25345
25915
|
};
|
|
25346
25916
|
}
|
|
25347
25917
|
function isValidationEvent(value) {
|
|
25348
|
-
return
|
|
25918
|
+
return isRecord2(value) && value.eventType === "Microsoft.EventGrid.SubscriptionValidationEvent";
|
|
25349
25919
|
}
|
|
25350
25920
|
function throwEventGridError(response, body, inv) {
|
|
25351
25921
|
const redacted = body.replace(/[A-Za-z0-9+/]{32,}={0,2}/g, "[redacted]").slice(0, 500);
|
|
@@ -25436,7 +26006,7 @@ function retryAfterMs4(value) {
|
|
|
25436
26006
|
const date4 = Date.parse(value);
|
|
25437
26007
|
return Number.isFinite(date4) ? Math.max(0, date4 - Date.now()) : 6e4;
|
|
25438
26008
|
}
|
|
25439
|
-
function
|
|
26009
|
+
function isRecord2(value) {
|
|
25440
26010
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
25441
26011
|
}
|
|
25442
26012
|
|
|
@@ -25842,7 +26412,7 @@ var BROKER_PROPERTY_NAMES = /* @__PURE__ */ new Set([
|
|
|
25842
26412
|
]);
|
|
25843
26413
|
function allowedBrokerProperties(value) {
|
|
25844
26414
|
if (value === void 0) return void 0;
|
|
25845
|
-
if (!
|
|
26415
|
+
if (!isRecord3(value)) throw new Error("brokerProperties must be an object");
|
|
25846
26416
|
const entries = Object.entries(value);
|
|
25847
26417
|
if (entries.length > BROKER_PROPERTY_NAMES.size) throw new Error("brokerProperties contains too many fields");
|
|
25848
26418
|
const result = {};
|
|
@@ -25865,7 +26435,7 @@ var RESERVED_HEADERS = /* @__PURE__ */ new Set([
|
|
|
25865
26435
|
]);
|
|
25866
26436
|
function applicationPropertyHeaders(value) {
|
|
25867
26437
|
if (value === void 0) return {};
|
|
25868
|
-
if (!
|
|
26438
|
+
if (!isRecord3(value)) throw new Error("applicationProperties must be an object");
|
|
25869
26439
|
const entries = Object.entries(value);
|
|
25870
26440
|
if (entries.length > 64) throw new Error("applicationProperties cannot contain more than 64 entries");
|
|
25871
26441
|
const result = {};
|
|
@@ -25997,7 +26567,7 @@ function retryAfterMs5(value) {
|
|
|
25997
26567
|
function redactProviderText(value) {
|
|
25998
26568
|
return value.replace(/SharedAccessSignature\s+[^\s<]+/gi, "SharedAccessSignature [redacted]").replace(/SharedAccessKey=[^;\s<]+/gi, "SharedAccessKey=[redacted]");
|
|
25999
26569
|
}
|
|
26000
|
-
function
|
|
26570
|
+
function isRecord3(value) {
|
|
26001
26571
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26002
26572
|
}
|
|
26003
26573
|
|
|
@@ -40326,7 +40896,7 @@ function parseServiceAccountCredentials(credentials, dataSourceId) {
|
|
|
40326
40896
|
} catch {
|
|
40327
40897
|
throw new CredentialsExpired("Google Pub/Sub service-account key must be valid JSON", dataSourceId);
|
|
40328
40898
|
}
|
|
40329
|
-
if (!
|
|
40899
|
+
if (!isRecord4(parsed) || parsed.type !== "service_account") {
|
|
40330
40900
|
throw new CredentialsExpired("Google Pub/Sub requires a Google service_account key", dataSourceId);
|
|
40331
40901
|
}
|
|
40332
40902
|
const projectId = requiredCredentialString(parsed.project_id, "project_id", dataSourceId);
|
|
@@ -40386,8 +40956,8 @@ async function exchangeAccessToken(credentials, dataSourceId, cacheKey) {
|
|
|
40386
40956
|
body: form2
|
|
40387
40957
|
});
|
|
40388
40958
|
const body = await readJson(response, 64 * 1024);
|
|
40389
|
-
if (!response.ok || !
|
|
40390
|
-
const reason =
|
|
40959
|
+
if (!response.ok || !isRecord4(body) || typeof body.access_token !== "string" || body.access_token.trim() === "") {
|
|
40960
|
+
const reason = isRecord4(body) && typeof body.error === "string" ? body.error : void 0;
|
|
40391
40961
|
throw new CredentialsExpired("Google rejected the Pub/Sub service-account grant", dataSourceId, {
|
|
40392
40962
|
status: response.status,
|
|
40393
40963
|
reason,
|
|
@@ -40425,7 +40995,7 @@ async function pubSubRequest(credentials, inv, method, path, query2, body) {
|
|
|
40425
40995
|
});
|
|
40426
40996
|
const responseBody = await readJson(response, MAX_RESPONSE_BYTES4);
|
|
40427
40997
|
if (response.ok) return responseBody;
|
|
40428
|
-
const providerError =
|
|
40998
|
+
const providerError = isRecord4(responseBody) && isRecord4(responseBody.error) ? responseBody.error : void 0;
|
|
40429
40999
|
const reason = providerError && typeof providerError.status === "string" ? providerError.status : void 0;
|
|
40430
41000
|
if (response.status === 401) {
|
|
40431
41001
|
throw new CredentialsExpired("Google Pub/Sub credentials were rejected", inv.source.id, {
|
|
@@ -40506,7 +41076,7 @@ function publishMessages(value) {
|
|
|
40506
41076
|
throw new Error("messages must contain 1 to 1000 entries");
|
|
40507
41077
|
}
|
|
40508
41078
|
return value.map((entry, index) => {
|
|
40509
|
-
if (!
|
|
41079
|
+
if (!isRecord4(entry)) throw new Error(`messages[${index}] must be an object`);
|
|
40510
41080
|
const hasText = typeof entry.text === "string";
|
|
40511
41081
|
const hasData = typeof entry.data === "string";
|
|
40512
41082
|
if (hasText === hasData) throw new Error(`messages[${index}] requires exactly one of text or data`);
|
|
@@ -40577,7 +41147,7 @@ function optionalStringArray(value, field) {
|
|
|
40577
41147
|
}
|
|
40578
41148
|
function optionalStringMap(value, field) {
|
|
40579
41149
|
if (value === void 0) return void 0;
|
|
40580
|
-
if (!
|
|
41150
|
+
if (!isRecord4(value)) throw new Error(`${field} must be an object of strings`);
|
|
40581
41151
|
const entries = Object.entries(value);
|
|
40582
41152
|
if (entries.length > 64) throw new Error(`${field} cannot contain more than 64 entries`);
|
|
40583
41153
|
return Object.fromEntries(entries.map(([key, entry]) => [
|
|
@@ -40623,7 +41193,7 @@ function retryAfterMs6(value) {
|
|
|
40623
41193
|
const date4 = Date.parse(value);
|
|
40624
41194
|
return Number.isFinite(date4) ? Math.max(0, date4 - Date.now()) : 6e4;
|
|
40625
41195
|
}
|
|
40626
|
-
function
|
|
41196
|
+
function isRecord4(value) {
|
|
40627
41197
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
40628
41198
|
}
|
|
40629
41199
|
|
|
@@ -74479,7 +75049,7 @@ var zohoBookingsConnector = {
|
|
|
74479
75049
|
break;
|
|
74480
75050
|
case "appointments.list": {
|
|
74481
75051
|
requireString(inv.args, "from_time");
|
|
74482
|
-
const filters =
|
|
75052
|
+
const filters = pick3(inv.args, ["from_time", "to_time", "service_id", "staff_id", "status"]);
|
|
74483
75053
|
data4 = await request2(inv.source, "/fetchappointment", {
|
|
74484
75054
|
method: "POST",
|
|
74485
75055
|
body: form({ data: JSON.stringify(filters) })
|
|
@@ -74514,7 +75084,7 @@ var zohoBookingsConnector = {
|
|
|
74514
75084
|
for (const key of ["service_id", "from_time"]) requireString(inv.args, key);
|
|
74515
75085
|
const customer = requireObject(inv.args, "customer_details");
|
|
74516
75086
|
exactlyOne(inv.args, ["staff_id", "resource_id", "group_id"], "booking assignment");
|
|
74517
|
-
const fields =
|
|
75087
|
+
const fields = pick3(inv.args, [
|
|
74518
75088
|
"service_id",
|
|
74519
75089
|
"from_time",
|
|
74520
75090
|
"staff_id",
|
|
@@ -74536,7 +75106,7 @@ var zohoBookingsConnector = {
|
|
|
74536
75106
|
exactlyOne(inv.args, ["start_time", "staff_id", "group_id"], "reschedule target");
|
|
74537
75107
|
data4 = await request2(inv.source, "/rescheduleappointment", {
|
|
74538
75108
|
method: "POST",
|
|
74539
|
-
body: form(
|
|
75109
|
+
body: form(pick3(inv.args, ["booking_id", "start_time", "staff_id", "group_id"]))
|
|
74540
75110
|
});
|
|
74541
75111
|
break;
|
|
74542
75112
|
}
|
|
@@ -74646,7 +75216,7 @@ async function readBody2(response) {
|
|
|
74646
75216
|
}
|
|
74647
75217
|
function query(args, keys, required = []) {
|
|
74648
75218
|
for (const key of required) requireString(args, key);
|
|
74649
|
-
return form(
|
|
75219
|
+
return form(pick3(args, keys));
|
|
74650
75220
|
}
|
|
74651
75221
|
function form(values) {
|
|
74652
75222
|
const result = new URLSearchParams();
|
|
@@ -74655,7 +75225,7 @@ function form(values) {
|
|
|
74655
75225
|
}
|
|
74656
75226
|
return result;
|
|
74657
75227
|
}
|
|
74658
|
-
function
|
|
75228
|
+
function pick3(args, keys) {
|
|
74659
75229
|
return Object.fromEntries(keys.filter((key) => args[key] !== void 0).map((key) => [key, args[key]]));
|
|
74660
75230
|
}
|
|
74661
75231
|
function requireString(args, key) {
|
|
@@ -84204,6 +84774,8 @@ var biginByZohoOAuthEnvMap = {
|
|
|
84204
84774
|
clientSecret: "BIGIN_BY_ZOHO_OAUTH_CLIENT_SECRET"
|
|
84205
84775
|
};
|
|
84206
84776
|
var CONNECTOR_ADAPTER_FACTORIES = [
|
|
84777
|
+
// Optional deployment override. The credential-only singleton remains the default.
|
|
84778
|
+
defineFactoryAdapter(createPhonyConnector, { baseUrl: "PH0NY_API_URL" }),
|
|
84207
84779
|
defineFactoryAdapter(
|
|
84208
84780
|
googleCalendar,
|
|
84209
84781
|
googleOAuthEnvMap
|
|
@@ -84898,598 +85470,6 @@ function resolveConnectorAdapterFactoryOptions(definition, envSource) {
|
|
|
84898
85470
|
return options;
|
|
84899
85471
|
}
|
|
84900
85472
|
|
|
84901
|
-
// src/connectors/adapters/phony.ts
|
|
84902
|
-
var API15 = "https://api.ph0ny.com";
|
|
84903
|
-
var E164 = /^\+[1-9]\d{7,14}$/;
|
|
84904
|
-
var phonyConnector = {
|
|
84905
|
-
manifest: {
|
|
84906
|
-
kind: "phony",
|
|
84907
|
-
displayName: "ph0ny",
|
|
84908
|
-
description: "Place real outbound phone calls with a voice agent, then read back call status, transcript, and extracted fields. Outbound calls require recorded user consent and support a dry-run that validates the full configuration without dialing.",
|
|
84909
|
-
auth: {
|
|
84910
|
-
kind: "api-key",
|
|
84911
|
-
hint: "Paste your ph0ny API key (plabs_\u2026). Create one in the developer portal via POST /v1/keys \u2014 it is shown once."
|
|
84912
|
-
},
|
|
84913
|
-
category: "comms",
|
|
84914
|
-
// A call's status/transcript evolve while it is live, so a fetched row
|
|
84915
|
-
// can be stale moments later — reads are point-in-time, not authoritative
|
|
84916
|
-
// truth. start_outbound_call creates a fresh, uncontended call each time
|
|
84917
|
-
// (cas='none', fire-and-forget external effect; no upstream CAS exists).
|
|
84918
|
-
defaultConsistencyModel: "cache",
|
|
84919
|
-
capabilities: [
|
|
84920
|
-
{
|
|
84921
|
-
name: "list_agents",
|
|
84922
|
-
class: "read",
|
|
84923
|
-
description: "List the voice agents on your ph0ny developer account (newest first).",
|
|
84924
|
-
parameters: {
|
|
84925
|
-
type: "object",
|
|
84926
|
-
properties: {
|
|
84927
|
-
limit: { type: "integer", minimum: 1, maximum: 100, default: 20 },
|
|
84928
|
-
cursor: { type: "string", description: "Pagination cursor from a prior response." }
|
|
84929
|
-
}
|
|
84930
|
-
}
|
|
84931
|
-
},
|
|
84932
|
-
{
|
|
84933
|
-
name: "get_call",
|
|
84934
|
-
class: "read",
|
|
84935
|
-
description: "Fetch a single outbound call by id, including status, transcript, and extracted fields.",
|
|
84936
|
-
parameters: {
|
|
84937
|
-
type: "object",
|
|
84938
|
-
properties: {
|
|
84939
|
-
id: { type: "string", description: "Outbound call id returned by start_outbound_call." }
|
|
84940
|
-
},
|
|
84941
|
-
required: ["id"]
|
|
84942
|
-
}
|
|
84943
|
-
},
|
|
84944
|
-
{
|
|
84945
|
-
name: "list_calls",
|
|
84946
|
-
class: "read",
|
|
84947
|
-
description: "List recent outbound calls for the account, optionally filtered by agent.",
|
|
84948
|
-
parameters: {
|
|
84949
|
-
type: "object",
|
|
84950
|
-
properties: {
|
|
84951
|
-
agentId: { type: "string", description: "Optional filter \u2014 only calls placed by this agent." },
|
|
84952
|
-
limit: { type: "integer", minimum: 1, maximum: 50, default: 20 }
|
|
84953
|
-
}
|
|
84954
|
-
}
|
|
84955
|
-
},
|
|
84956
|
-
{
|
|
84957
|
-
name: "start_outbound_call",
|
|
84958
|
-
class: "mutation",
|
|
84959
|
-
description: "Place an outbound phone call. Requires userConsentRecorded=true (the user must have explicitly authorized the call). Set dryRun=true to validate the full configuration without dialing.",
|
|
84960
|
-
cas: "none",
|
|
84961
|
-
externalEffect: true,
|
|
84962
|
-
parameters: {
|
|
84963
|
-
type: "object",
|
|
84964
|
-
properties: {
|
|
84965
|
-
agentId: { type: "string", description: "Agent that will place the call (agentKind must be personal_assistant)." },
|
|
84966
|
-
toNumber: { type: "string", description: "E.164 destination, e.g. +14155551212." },
|
|
84967
|
-
fromNumber: { type: "string", description: "E.164 caller number provisioned to your developer account." },
|
|
84968
|
-
mission: {
|
|
84969
|
-
type: "object",
|
|
84970
|
-
description: "What the agent should accomplish on the call.",
|
|
84971
|
-
properties: {
|
|
84972
|
-
goal: { type: "string", description: "Plain-language objective (8\u20132000 chars)." },
|
|
84973
|
-
successSchema: { type: "object", description: "Optional JSON schema describing fields to extract on success." },
|
|
84974
|
-
ivrHints: {
|
|
84975
|
-
type: "array",
|
|
84976
|
-
items: { type: "string" },
|
|
84977
|
-
description: "Optional hints for navigating phone-tree / IVR menus (\u22648 entries)."
|
|
84978
|
-
},
|
|
84979
|
-
maxTurns: { type: "integer", minimum: 1, maximum: 60 },
|
|
84980
|
-
maxDurationMs: { type: "integer", minimum: 3e4, maximum: 12e5 }
|
|
84981
|
-
},
|
|
84982
|
-
required: ["goal"]
|
|
84983
|
-
},
|
|
84984
|
-
missionId: { type: "string", description: "Optional caller-supplied mission id." },
|
|
84985
|
-
callerProfile: {
|
|
84986
|
-
type: "object",
|
|
84987
|
-
properties: {
|
|
84988
|
-
userName: { type: "string" },
|
|
84989
|
-
companyName: { type: "string" }
|
|
84990
|
-
}
|
|
84991
|
-
},
|
|
84992
|
-
voiceCloneId: { type: "string", description: "Optional cloned-voice id to speak with." },
|
|
84993
|
-
userConsentRecorded: {
|
|
84994
|
-
type: "boolean",
|
|
84995
|
-
description: "REQUIRED. Must be true \u2014 the user explicitly authorized this call. ph0ny rejects false."
|
|
84996
|
-
},
|
|
84997
|
-
dryRun: {
|
|
84998
|
-
type: "boolean",
|
|
84999
|
-
description: "When true, validate every gate and return a dryRunReport without placing the call."
|
|
85000
|
-
}
|
|
85001
|
-
},
|
|
85002
|
-
required: ["agentId", "toNumber", "fromNumber", "mission", "userConsentRecorded"]
|
|
85003
|
-
}
|
|
85004
|
-
},
|
|
85005
|
-
{
|
|
85006
|
-
name: "create_agent",
|
|
85007
|
-
class: "mutation",
|
|
85008
|
-
description: "Create a voice agent on your ph0ny developer account. Only name is required; the rest configure the LLM, TTS, voice, knowledge base, and capture behavior.",
|
|
85009
|
-
cas: "none",
|
|
85010
|
-
externalEffect: true,
|
|
85011
|
-
parameters: {
|
|
85012
|
-
type: "object",
|
|
85013
|
-
properties: {
|
|
85014
|
-
id: { type: "string", description: "Optional caller-supplied agent id (1\u201360 chars)." },
|
|
85015
|
-
name: { type: "string", description: "Display name (1\u2013100 chars)." },
|
|
85016
|
-
description: { type: "string", description: "Optional description (\u22641000 chars)." },
|
|
85017
|
-
systemPrompt: { type: "string", description: "Optional system prompt (\u226410000 chars)." },
|
|
85018
|
-
firstMessage: { type: "string", description: "Optional opening line the agent speaks first (\u22641000 chars)." },
|
|
85019
|
-
voiceId: { type: "string", description: "Optional TTS voice id." },
|
|
85020
|
-
ttsProvider: { type: "string", description: 'TTS provider; "default" lets ph0ny choose.' },
|
|
85021
|
-
ttsModel: { type: "string", description: "Optional TTS model id." },
|
|
85022
|
-
sttProvider: { type: "string", description: "Optional speech-to-text provider." },
|
|
85023
|
-
llmProvider: { type: "string", description: "LLM provider (e.g. openai, anthropic). Defaults to openai server-side." },
|
|
85024
|
-
llmModel: { type: "string", description: "Optional LLM model id." },
|
|
85025
|
-
libraryId: { type: "string", description: "Ingest-service library id (preferred over collectionId)." },
|
|
85026
|
-
collectionId: { type: "string", description: "Deprecated \u2014 KB collection id to attach. Prefer libraryId." },
|
|
85027
|
-
styleProfile: { type: "object", description: "Optional style profile object." },
|
|
85028
|
-
promptByModel: { type: "object", description: "Optional per-model prompt overrides (modelId \u2192 prompt)." },
|
|
85029
|
-
language: { type: "string", description: 'BCP-47 language code; defaults to "en".' },
|
|
85030
|
-
temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature (0\u20132); defaults to 0.7." },
|
|
85031
|
-
maxTokens: { type: "integer", minimum: 1, maximum: 16384, description: "Optional max output tokens." },
|
|
85032
|
-
contactCaptureEnabled: { type: "boolean", description: "Enable structured contact capture during calls." },
|
|
85033
|
-
contactCaptureFields: {
|
|
85034
|
-
type: "array",
|
|
85035
|
-
items: { type: "string", enum: ["name", "email", "phone"] },
|
|
85036
|
-
description: "Which contact fields to capture when capture is enabled."
|
|
85037
|
-
},
|
|
85038
|
-
metadata: { type: "object", description: "Arbitrary metadata object stored with the agent." }
|
|
85039
|
-
},
|
|
85040
|
-
required: ["name"]
|
|
85041
|
-
}
|
|
85042
|
-
},
|
|
85043
|
-
{
|
|
85044
|
-
name: "provision_agent",
|
|
85045
|
-
class: "mutation",
|
|
85046
|
-
description: "Create an agent and, in the same atomic call, optionally a fresh knowledge-base collection and seed it with initial content. Returns the agent plus the created collection and ingestion results.",
|
|
85047
|
-
cas: "none",
|
|
85048
|
-
externalEffect: true,
|
|
85049
|
-
parameters: {
|
|
85050
|
-
type: "object",
|
|
85051
|
-
properties: {
|
|
85052
|
-
id: { type: "string", description: "Optional caller-supplied agent id (1\u201360 chars)." },
|
|
85053
|
-
name: { type: "string", description: "Display name (1\u2013100 chars)." },
|
|
85054
|
-
description: { type: "string", description: "Optional description (\u22641000 chars)." },
|
|
85055
|
-
systemPrompt: { type: "string", description: "Optional system prompt (\u226410000 chars)." },
|
|
85056
|
-
firstMessage: { type: "string", description: "Optional opening line the agent speaks first (\u22641000 chars)." },
|
|
85057
|
-
voiceId: { type: "string", description: "Optional TTS voice id." },
|
|
85058
|
-
ttsProvider: { type: "string", description: 'TTS provider; "default" lets ph0ny choose.' },
|
|
85059
|
-
ttsModel: { type: "string", description: "Optional TTS model id." },
|
|
85060
|
-
sttProvider: { type: "string", description: "Optional speech-to-text provider." },
|
|
85061
|
-
llmProvider: { type: "string", description: "LLM provider (e.g. openai, anthropic). Defaults to openai server-side." },
|
|
85062
|
-
llmModel: { type: "string", description: "Optional LLM model id." },
|
|
85063
|
-
libraryId: { type: "string", description: "Ingest-service library id (preferred over collectionId)." },
|
|
85064
|
-
collectionId: { type: "string", description: "Existing KB collection id to attach instead of creating one." },
|
|
85065
|
-
styleProfile: { type: "object", description: "Optional style profile object." },
|
|
85066
|
-
promptByModel: { type: "object", description: "Optional per-model prompt overrides (modelId \u2192 prompt)." },
|
|
85067
|
-
language: { type: "string", description: 'BCP-47 language code; defaults to "en".' },
|
|
85068
|
-
temperature: { type: "number", minimum: 0, maximum: 2, description: "Sampling temperature (0\u20132); defaults to 0.7." },
|
|
85069
|
-
maxTokens: { type: "integer", minimum: 1, maximum: 16384, description: "Optional max output tokens." },
|
|
85070
|
-
contactCaptureEnabled: { type: "boolean", description: "Enable structured contact capture during calls." },
|
|
85071
|
-
contactCaptureFields: {
|
|
85072
|
-
type: "array",
|
|
85073
|
-
items: { type: "string", enum: ["name", "email", "phone"] },
|
|
85074
|
-
description: "Which contact fields to capture when capture is enabled."
|
|
85075
|
-
},
|
|
85076
|
-
metadata: { type: "object", description: "Arbitrary metadata object stored with the agent." },
|
|
85077
|
-
collection: {
|
|
85078
|
-
type: "object",
|
|
85079
|
-
description: "When present, create a fresh KB collection and attach it to the agent.",
|
|
85080
|
-
properties: {
|
|
85081
|
-
name: { type: "string", description: "Collection name (1\u2013100 chars)." },
|
|
85082
|
-
description: { type: "string", description: "Optional collection description (\u2264500 chars)." }
|
|
85083
|
-
},
|
|
85084
|
-
required: ["name"]
|
|
85085
|
-
},
|
|
85086
|
-
initialContent: {
|
|
85087
|
-
type: "array",
|
|
85088
|
-
maxItems: 10,
|
|
85089
|
-
description: "Documents to ingest into the new/attached collection (\u226410).",
|
|
85090
|
-
items: {
|
|
85091
|
-
type: "object",
|
|
85092
|
-
properties: {
|
|
85093
|
-
content: { type: "string", description: "Raw text or transcript content." },
|
|
85094
|
-
contentType: { type: "string", enum: ["text", "transcript"], description: 'Defaults to "text".' },
|
|
85095
|
-
metadata: { type: "object", description: "Arbitrary metadata stored with the document." }
|
|
85096
|
-
},
|
|
85097
|
-
required: ["content"]
|
|
85098
|
-
}
|
|
85099
|
-
}
|
|
85100
|
-
},
|
|
85101
|
-
required: ["name"]
|
|
85102
|
-
}
|
|
85103
|
-
},
|
|
85104
|
-
{
|
|
85105
|
-
name: "kb_create_collection",
|
|
85106
|
-
class: "mutation",
|
|
85107
|
-
description: "Create a knowledge-base collection the agent can search at call time.",
|
|
85108
|
-
cas: "none",
|
|
85109
|
-
externalEffect: true,
|
|
85110
|
-
parameters: {
|
|
85111
|
-
type: "object",
|
|
85112
|
-
properties: {
|
|
85113
|
-
name: { type: "string", description: "Collection name (1\u2013100 chars)." },
|
|
85114
|
-
description: { type: "string", description: "Optional description (\u2264500 chars)." },
|
|
85115
|
-
metadata: { type: "object", description: "Arbitrary metadata object stored with the collection." }
|
|
85116
|
-
},
|
|
85117
|
-
required: ["name"]
|
|
85118
|
-
}
|
|
85119
|
-
},
|
|
85120
|
-
{
|
|
85121
|
-
name: "kb_ingest",
|
|
85122
|
-
class: "mutation",
|
|
85123
|
-
description: 'Chunk, embed, and store content into a KB collection. Provide content directly (\u22641MB), or a sourceUrl for audio transcription. contentType "url" is not yet implemented server-side (501).',
|
|
85124
|
-
cas: "none",
|
|
85125
|
-
externalEffect: true,
|
|
85126
|
-
parameters: {
|
|
85127
|
-
type: "object",
|
|
85128
|
-
properties: {
|
|
85129
|
-
collectionId: { type: "string", description: "Target collection id." },
|
|
85130
|
-
content: { type: "string", description: "Raw text/transcript content (\u22641,000,000 chars)." },
|
|
85131
|
-
sourceUrl: { type: "string", description: 'Public URL \u2014 required when contentType is "audio" (transcribed) or "url".' },
|
|
85132
|
-
contentType: {
|
|
85133
|
-
type: "string",
|
|
85134
|
-
enum: ["text", "transcript", "audio", "url"],
|
|
85135
|
-
description: 'Defaults to "text". "audio" transcribes sourceUrl; "url" returns 501.'
|
|
85136
|
-
},
|
|
85137
|
-
metadata: { type: "object", description: "Arbitrary metadata stored with the document." },
|
|
85138
|
-
chunkSize: { type: "integer", minimum: 100, maximum: 4e3, description: "Chunk size in chars; defaults to 1000." },
|
|
85139
|
-
chunkOverlap: { type: "integer", minimum: 0, maximum: 500, description: "Chunk overlap in chars; defaults to 200. Must be < chunkSize." }
|
|
85140
|
-
},
|
|
85141
|
-
required: ["collectionId"]
|
|
85142
|
-
}
|
|
85143
|
-
},
|
|
85144
|
-
{
|
|
85145
|
-
name: "kb_search",
|
|
85146
|
-
class: "read",
|
|
85147
|
-
description: "Hybrid vector + keyword search over a KB collection. Returns ranked results with scores.",
|
|
85148
|
-
parameters: {
|
|
85149
|
-
type: "object",
|
|
85150
|
-
properties: {
|
|
85151
|
-
collectionId: { type: "string", description: "Collection id to search." },
|
|
85152
|
-
query: { type: "string", description: "Search query (1\u20132000 chars)." },
|
|
85153
|
-
limit: { type: "integer", minimum: 1, maximum: 100, description: "Max results; defaults to 10." },
|
|
85154
|
-
threshold: { type: "number", minimum: 0, maximum: 1, description: "Minimum similarity score; defaults to 0.7." },
|
|
85155
|
-
includeMetadata: { type: "boolean", description: "Include per-result metadata; defaults to true." }
|
|
85156
|
-
},
|
|
85157
|
-
required: ["collectionId", "query"]
|
|
85158
|
-
}
|
|
85159
|
-
}
|
|
85160
|
-
]
|
|
85161
|
-
},
|
|
85162
|
-
async executeRead(inv) {
|
|
85163
|
-
const token = bearerToken(inv.source.credentials);
|
|
85164
|
-
if (inv.capabilityName === "list_agents") {
|
|
85165
|
-
const { limit, cursor } = inv.args;
|
|
85166
|
-
const params = new URLSearchParams();
|
|
85167
|
-
params.set("limit", String(Math.min(Math.max(1, limit ?? 20), 100)));
|
|
85168
|
-
if (cursor) params.set("cursor", cursor);
|
|
85169
|
-
const json = await getJson(
|
|
85170
|
-
inv,
|
|
85171
|
-
token,
|
|
85172
|
-
`${API15}/v1/agents?${params.toString()}`,
|
|
85173
|
-
"list_agents"
|
|
85174
|
-
);
|
|
85175
|
-
return {
|
|
85176
|
-
data: { agents: json.data ?? [], nextCursor: json.nextCursor ?? null, hasMore: json.hasMore ?? false },
|
|
85177
|
-
fetchedAt: Date.now()
|
|
85178
|
-
};
|
|
85179
|
-
}
|
|
85180
|
-
if (inv.capabilityName === "get_call") {
|
|
85181
|
-
const { id: id7 } = inv.args;
|
|
85182
|
-
const json = await getJson(
|
|
85183
|
-
inv,
|
|
85184
|
-
token,
|
|
85185
|
-
`${API15}/v1/outbound/${encodeURIComponent(id7)}`,
|
|
85186
|
-
"get_call"
|
|
85187
|
-
);
|
|
85188
|
-
return { data: { call: json.call ?? null }, fetchedAt: Date.now() };
|
|
85189
|
-
}
|
|
85190
|
-
if (inv.capabilityName === "list_calls") {
|
|
85191
|
-
const { agentId, limit } = inv.args;
|
|
85192
|
-
const params = new URLSearchParams();
|
|
85193
|
-
params.set("limit", String(Math.min(Math.max(1, limit ?? 20), 50)));
|
|
85194
|
-
if (agentId) params.set("agentId", agentId);
|
|
85195
|
-
const json = await getJson(
|
|
85196
|
-
inv,
|
|
85197
|
-
token,
|
|
85198
|
-
`${API15}/v1/outbound?${params.toString()}`,
|
|
85199
|
-
"list_calls"
|
|
85200
|
-
);
|
|
85201
|
-
return { data: { calls: json.calls ?? [] }, fetchedAt: Date.now() };
|
|
85202
|
-
}
|
|
85203
|
-
if (inv.capabilityName === "kb_search") {
|
|
85204
|
-
const { collectionId, query: query2, limit, threshold, includeMetadata } = inv.args;
|
|
85205
|
-
const payload = { query: query2 };
|
|
85206
|
-
if (limit !== void 0) payload.limit = limit;
|
|
85207
|
-
if (threshold !== void 0) payload.threshold = threshold;
|
|
85208
|
-
if (includeMetadata !== void 0) payload.includeMetadata = includeMetadata;
|
|
85209
|
-
const res = await fetch(`${API15}/v1/collections/${encodeURIComponent(collectionId)}/search`, {
|
|
85210
|
-
method: "POST",
|
|
85211
|
-
headers: {
|
|
85212
|
-
authorization: `Bearer ${token}`,
|
|
85213
|
-
"content-type": "application/json"
|
|
85214
|
-
},
|
|
85215
|
-
body: JSON.stringify(payload),
|
|
85216
|
-
signal: AbortSignal.timeout(15e3)
|
|
85217
|
-
});
|
|
85218
|
-
if (res.status === 401) throw new CredentialsExpired("ph0ny rejected credentials (401)", inv.source.id);
|
|
85219
|
-
if (!res.ok) {
|
|
85220
|
-
const text2 = await res.text().catch(() => "");
|
|
85221
|
-
throw new Error(`phony kb_search ${res.status}: ${text2.slice(0, 200)}`);
|
|
85222
|
-
}
|
|
85223
|
-
const json = await res.json();
|
|
85224
|
-
return {
|
|
85225
|
-
data: {
|
|
85226
|
-
results: json.results ?? [],
|
|
85227
|
-
queryTokens: json.queryTokens ?? 0,
|
|
85228
|
-
...json.graphContext !== void 0 ? { graphContext: json.graphContext } : {}
|
|
85229
|
-
},
|
|
85230
|
-
fetchedAt: Date.now()
|
|
85231
|
-
};
|
|
85232
|
-
}
|
|
85233
|
-
throw new Error(`phony: unknown read capability ${inv.capabilityName}`);
|
|
85234
|
-
},
|
|
85235
|
-
async executeMutation(inv) {
|
|
85236
|
-
const token = bearerToken(inv.source.credentials);
|
|
85237
|
-
if (inv.capabilityName === "start_outbound_call") {
|
|
85238
|
-
const args = validateOutboundStartArgs(inv.args);
|
|
85239
|
-
const payload = {
|
|
85240
|
-
agentId: args.agentId,
|
|
85241
|
-
toNumber: args.toNumber,
|
|
85242
|
-
fromNumber: args.fromNumber,
|
|
85243
|
-
mission: args.mission,
|
|
85244
|
-
userConsentRecorded: args.userConsentRecorded
|
|
85245
|
-
};
|
|
85246
|
-
if (args.missionId !== void 0) payload.missionId = args.missionId;
|
|
85247
|
-
if (args.callerProfile !== void 0) payload.callerProfile = args.callerProfile;
|
|
85248
|
-
if (args.voiceCloneId !== void 0) payload.voiceCloneId = args.voiceCloneId;
|
|
85249
|
-
if (args.dryRun !== void 0) payload.dryRun = args.dryRun;
|
|
85250
|
-
const res = await fetch(`${API15}/v1/outbound/start`, {
|
|
85251
|
-
method: "POST",
|
|
85252
|
-
headers: {
|
|
85253
|
-
authorization: `Bearer ${token}`,
|
|
85254
|
-
"content-type": "application/json"
|
|
85255
|
-
},
|
|
85256
|
-
body: JSON.stringify(payload),
|
|
85257
|
-
signal: AbortSignal.timeout(2e4)
|
|
85258
|
-
});
|
|
85259
|
-
if (res.status === 401) throw new CredentialsExpired("ph0ny rejected credentials (401)", inv.source.id);
|
|
85260
|
-
if (!res.ok) {
|
|
85261
|
-
const text2 = await res.text().catch(() => "");
|
|
85262
|
-
throw new Error(`phony start_outbound_call ${res.status}: ${text2.slice(0, 200)}`);
|
|
85263
|
-
}
|
|
85264
|
-
const json = await res.json();
|
|
85265
|
-
return {
|
|
85266
|
-
status: "committed",
|
|
85267
|
-
data: {
|
|
85268
|
-
callId: json.callId,
|
|
85269
|
-
callSid: json.callSid,
|
|
85270
|
-
callStatus: json.status,
|
|
85271
|
-
dryRun: json.dryRun ?? false,
|
|
85272
|
-
...json.dryRunReport !== void 0 ? { dryRunReport: json.dryRunReport } : {}
|
|
85273
|
-
},
|
|
85274
|
-
committedAt: Date.now(),
|
|
85275
|
-
idempotentReplay: false
|
|
85276
|
-
};
|
|
85277
|
-
}
|
|
85278
|
-
if (inv.capabilityName === "create_agent") {
|
|
85279
|
-
const payload = pick3(inv.args, AGENT_FIELDS);
|
|
85280
|
-
const json = await postJson(inv, token, `${API15}/v1/agents`, payload, "create_agent");
|
|
85281
|
-
return {
|
|
85282
|
-
status: "committed",
|
|
85283
|
-
data: { agent: json },
|
|
85284
|
-
committedAt: Date.now(),
|
|
85285
|
-
idempotentReplay: false
|
|
85286
|
-
};
|
|
85287
|
-
}
|
|
85288
|
-
if (inv.capabilityName === "provision_agent") {
|
|
85289
|
-
const args = inv.args;
|
|
85290
|
-
const payload = pick3(args, AGENT_FIELDS);
|
|
85291
|
-
if (args.collection !== void 0) payload.collection = args.collection;
|
|
85292
|
-
if (args.initialContent !== void 0) payload.initialContent = args.initialContent;
|
|
85293
|
-
const json = await postJson(
|
|
85294
|
-
inv,
|
|
85295
|
-
token,
|
|
85296
|
-
`${API15}/v1/agents/provision`,
|
|
85297
|
-
payload,
|
|
85298
|
-
"provision_agent"
|
|
85299
|
-
);
|
|
85300
|
-
return {
|
|
85301
|
-
status: "committed",
|
|
85302
|
-
data: {
|
|
85303
|
-
agent: json.agent ?? null,
|
|
85304
|
-
...json.collection !== void 0 ? { collection: json.collection } : {},
|
|
85305
|
-
...json.ingested !== void 0 ? { ingested: json.ingested } : {}
|
|
85306
|
-
},
|
|
85307
|
-
committedAt: Date.now(),
|
|
85308
|
-
idempotentReplay: false
|
|
85309
|
-
};
|
|
85310
|
-
}
|
|
85311
|
-
if (inv.capabilityName === "kb_create_collection") {
|
|
85312
|
-
const { name, description, metadata } = inv.args;
|
|
85313
|
-
const payload = { name };
|
|
85314
|
-
if (description !== void 0) payload.description = description;
|
|
85315
|
-
if (metadata !== void 0) payload.metadata = metadata;
|
|
85316
|
-
const json = await postJson(inv, token, `${API15}/v1/collections`, payload, "kb_create_collection");
|
|
85317
|
-
return {
|
|
85318
|
-
status: "committed",
|
|
85319
|
-
data: { collection: json },
|
|
85320
|
-
committedAt: Date.now(),
|
|
85321
|
-
idempotentReplay: false
|
|
85322
|
-
};
|
|
85323
|
-
}
|
|
85324
|
-
if (inv.capabilityName === "kb_ingest") {
|
|
85325
|
-
const { collectionId, ...rest } = inv.args;
|
|
85326
|
-
const payload = pick3(rest, INGEST_FIELDS);
|
|
85327
|
-
const json = await postJson(
|
|
85328
|
-
inv,
|
|
85329
|
-
token,
|
|
85330
|
-
`${API15}/v1/collections/${encodeURIComponent(collectionId)}/ingest`,
|
|
85331
|
-
payload,
|
|
85332
|
-
"kb_ingest"
|
|
85333
|
-
);
|
|
85334
|
-
return {
|
|
85335
|
-
status: "committed",
|
|
85336
|
-
data: {
|
|
85337
|
-
documentId: json.documentId ?? null,
|
|
85338
|
-
chunksCreated: json.chunksCreated ?? 0,
|
|
85339
|
-
tokensUsed: json.tokensUsed ?? 0
|
|
85340
|
-
},
|
|
85341
|
-
committedAt: Date.now(),
|
|
85342
|
-
idempotentReplay: false
|
|
85343
|
-
};
|
|
85344
|
-
}
|
|
85345
|
-
throw new Error(`phony: unknown mutation capability ${inv.capabilityName}`);
|
|
85346
|
-
},
|
|
85347
|
-
async test(source) {
|
|
85348
|
-
try {
|
|
85349
|
-
const token = bearerToken(source.credentials);
|
|
85350
|
-
const res = await fetch(`${API15}/v1/outbound?limit=1`, {
|
|
85351
|
-
headers: { authorization: `Bearer ${token}` },
|
|
85352
|
-
signal: AbortSignal.timeout(8e3)
|
|
85353
|
-
});
|
|
85354
|
-
if (res.status === 401) return { ok: false, reason: "ph0ny rejected credentials (401) \u2014 reconnect required" };
|
|
85355
|
-
if (!res.ok) return { ok: false, reason: `ph0ny returned ${res.status}` };
|
|
85356
|
-
return { ok: true };
|
|
85357
|
-
} catch (err) {
|
|
85358
|
-
return { ok: false, reason: err instanceof Error ? err.message : String(err) };
|
|
85359
|
-
}
|
|
85360
|
-
}
|
|
85361
|
-
};
|
|
85362
|
-
function bearerToken(creds) {
|
|
85363
|
-
if (creds.kind !== "api-key" || typeof creds.apiKey !== "string" || creds.apiKey.length === 0) {
|
|
85364
|
-
throw new Error("phony: expected api-key credentials");
|
|
85365
|
-
}
|
|
85366
|
-
return creds.apiKey;
|
|
85367
|
-
}
|
|
85368
|
-
function validateOutboundStartArgs(args) {
|
|
85369
|
-
if (args.userConsentRecorded !== true) {
|
|
85370
|
-
throw new Error(
|
|
85371
|
-
"phony start_outbound_call requires userConsentRecorded=true after explicit user authorization; refusing before contacting ph0ny"
|
|
85372
|
-
);
|
|
85373
|
-
}
|
|
85374
|
-
assertNonEmptyString(args.agentId, "agentId", 128);
|
|
85375
|
-
assertE164(args.toNumber, "toNumber");
|
|
85376
|
-
assertE164(args.fromNumber, "fromNumber");
|
|
85377
|
-
if (!isRecord4(args.mission)) throw new Error("phony start_outbound_call requires mission");
|
|
85378
|
-
const mission = args.mission;
|
|
85379
|
-
assertNonEmptyString(mission.goal, "mission.goal", 2e3);
|
|
85380
|
-
if (mission.goal.trim().length < 8) throw new Error("phony start_outbound_call mission.goal must be at least 8 characters");
|
|
85381
|
-
if (mission.successSchema !== void 0 && !isRecord4(mission.successSchema)) {
|
|
85382
|
-
throw new Error("phony start_outbound_call mission.successSchema must be an object when supplied");
|
|
85383
|
-
}
|
|
85384
|
-
if (mission.ivrHints !== void 0) {
|
|
85385
|
-
if (!Array.isArray(mission.ivrHints) || mission.ivrHints.length > 8) {
|
|
85386
|
-
throw new Error("phony start_outbound_call mission.ivrHints must be an array with at most 8 entries");
|
|
85387
|
-
}
|
|
85388
|
-
for (const hint of mission.ivrHints) assertNonEmptyString(hint, "mission.ivrHints[]", 200);
|
|
85389
|
-
}
|
|
85390
|
-
assertOptionalInteger(mission.maxTurns, "mission.maxTurns", 1, 60);
|
|
85391
|
-
assertOptionalInteger(mission.maxDurationMs, "mission.maxDurationMs", 3e4, 20 * 60 * 1e3);
|
|
85392
|
-
if (args.missionId !== void 0) assertNonEmptyString(args.missionId, "missionId", 128);
|
|
85393
|
-
if (args.voiceCloneId !== void 0) assertNonEmptyString(args.voiceCloneId, "voiceCloneId", 128);
|
|
85394
|
-
if (args.callerProfile !== void 0) {
|
|
85395
|
-
if (!isRecord4(args.callerProfile)) throw new Error("phony start_outbound_call callerProfile must be an object");
|
|
85396
|
-
if (args.callerProfile.userName !== void 0) assertNonEmptyString(args.callerProfile.userName, "callerProfile.userName", 120);
|
|
85397
|
-
if (args.callerProfile.companyName !== void 0) {
|
|
85398
|
-
assertNonEmptyString(args.callerProfile.companyName, "callerProfile.companyName", 120);
|
|
85399
|
-
}
|
|
85400
|
-
}
|
|
85401
|
-
if (args.dryRun !== void 0 && typeof args.dryRun !== "boolean") {
|
|
85402
|
-
throw new Error("phony start_outbound_call dryRun must be boolean when supplied");
|
|
85403
|
-
}
|
|
85404
|
-
return args;
|
|
85405
|
-
}
|
|
85406
|
-
function assertNonEmptyString(value, field, maxLength) {
|
|
85407
|
-
if (typeof value !== "string" || value.trim().length === 0 || value.length > maxLength) {
|
|
85408
|
-
throw new Error(`phony start_outbound_call ${field} must be a non-empty string <= ${maxLength} chars`);
|
|
85409
|
-
}
|
|
85410
|
-
}
|
|
85411
|
-
function assertE164(value, field) {
|
|
85412
|
-
if (typeof value !== "string" || !E164.test(value)) {
|
|
85413
|
-
throw new Error(`phony start_outbound_call ${field} must be E.164 format, e.g. +14155550123`);
|
|
85414
|
-
}
|
|
85415
|
-
}
|
|
85416
|
-
function assertOptionalInteger(value, field, min, max) {
|
|
85417
|
-
if (value === void 0) return;
|
|
85418
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value < min || value > max) {
|
|
85419
|
-
throw new Error(`phony start_outbound_call ${field} must be an integer between ${min} and ${max}`);
|
|
85420
|
-
}
|
|
85421
|
-
}
|
|
85422
|
-
function isRecord4(value) {
|
|
85423
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
85424
|
-
}
|
|
85425
|
-
async function getJson(inv, token, url, label) {
|
|
85426
|
-
const res = await fetch(url, {
|
|
85427
|
-
headers: { authorization: `Bearer ${token}` },
|
|
85428
|
-
signal: AbortSignal.timeout(1e4)
|
|
85429
|
-
});
|
|
85430
|
-
if (res.status === 401) throw new CredentialsExpired("ph0ny rejected credentials (401)", inv.source.id);
|
|
85431
|
-
if (!res.ok) {
|
|
85432
|
-
const text2 = await res.text().catch(() => "");
|
|
85433
|
-
throw new Error(`phony ${label} ${res.status}: ${text2.slice(0, 200)}`);
|
|
85434
|
-
}
|
|
85435
|
-
return await res.json();
|
|
85436
|
-
}
|
|
85437
|
-
async function postJson(inv, token, url, payload, label) {
|
|
85438
|
-
const res = await fetch(url, {
|
|
85439
|
-
method: "POST",
|
|
85440
|
-
headers: {
|
|
85441
|
-
authorization: `Bearer ${token}`,
|
|
85442
|
-
"content-type": "application/json"
|
|
85443
|
-
},
|
|
85444
|
-
body: JSON.stringify(payload),
|
|
85445
|
-
signal: AbortSignal.timeout(2e4)
|
|
85446
|
-
});
|
|
85447
|
-
if (res.status === 401) throw new CredentialsExpired("ph0ny rejected credentials (401)", inv.source.id);
|
|
85448
|
-
if (!res.ok) {
|
|
85449
|
-
const text2 = await res.text().catch(() => "");
|
|
85450
|
-
throw new Error(`phony ${label} ${res.status}: ${text2.slice(0, 200)}`);
|
|
85451
|
-
}
|
|
85452
|
-
return await res.json();
|
|
85453
|
-
}
|
|
85454
|
-
function pick3(args, fields) {
|
|
85455
|
-
const out = {};
|
|
85456
|
-
for (const key of fields) {
|
|
85457
|
-
if (args[key] !== void 0) out[key] = args[key];
|
|
85458
|
-
}
|
|
85459
|
-
return out;
|
|
85460
|
-
}
|
|
85461
|
-
var AGENT_FIELDS = [
|
|
85462
|
-
"id",
|
|
85463
|
-
"name",
|
|
85464
|
-
"description",
|
|
85465
|
-
"systemPrompt",
|
|
85466
|
-
"firstMessage",
|
|
85467
|
-
"voiceId",
|
|
85468
|
-
"ttsProvider",
|
|
85469
|
-
"ttsModel",
|
|
85470
|
-
"sttProvider",
|
|
85471
|
-
"llmProvider",
|
|
85472
|
-
"llmModel",
|
|
85473
|
-
"libraryId",
|
|
85474
|
-
"collectionId",
|
|
85475
|
-
"styleProfile",
|
|
85476
|
-
"promptByModel",
|
|
85477
|
-
"language",
|
|
85478
|
-
"temperature",
|
|
85479
|
-
"maxTokens",
|
|
85480
|
-
"contactCaptureEnabled",
|
|
85481
|
-
"contactCaptureFields",
|
|
85482
|
-
"metadata"
|
|
85483
|
-
];
|
|
85484
|
-
var INGEST_FIELDS = [
|
|
85485
|
-
"content",
|
|
85486
|
-
"sourceUrl",
|
|
85487
|
-
"contentType",
|
|
85488
|
-
"metadata",
|
|
85489
|
-
"chunkSize",
|
|
85490
|
-
"chunkOverlap"
|
|
85491
|
-
];
|
|
85492
|
-
|
|
85493
85473
|
// src/connectors/adapters/webhook.ts
|
|
85494
85474
|
import { createHmac as createHmac6 } from "crypto";
|
|
85495
85475
|
var webhookConnector = {
|
|
@@ -150505,6 +150485,8 @@ export {
|
|
|
150505
150485
|
oauthClientCredentialRedactionValues,
|
|
150506
150486
|
redactOAuthSensitiveText,
|
|
150507
150487
|
_resetPendingFlowsForTests,
|
|
150488
|
+
createPhonyConnector,
|
|
150489
|
+
phonyConnector,
|
|
150508
150490
|
declarativeRestConnector,
|
|
150509
150491
|
oktaConnector,
|
|
150510
150492
|
clientCredentialsRestConnector,
|
|
@@ -150831,7 +150813,6 @@ export {
|
|
|
150831
150813
|
zerobounceConnector,
|
|
150832
150814
|
CONNECTOR_ADAPTER_FACTORIES,
|
|
150833
150815
|
resolveConnectorAdapterFactoryOptions,
|
|
150834
|
-
phonyConnector,
|
|
150835
150816
|
webhookConnector,
|
|
150836
150817
|
stripeWebhookReceiverConnector,
|
|
150837
150818
|
slackEventsConnector,
|
|
@@ -151168,4 +151149,4 @@ export {
|
|
|
151168
151149
|
sendblueConnector,
|
|
151169
151150
|
adapters_exports
|
|
151170
151151
|
};
|
|
151171
|
-
//# sourceMappingURL=chunk-
|
|
151152
|
+
//# sourceMappingURL=chunk-RFJSN3YW.js.map
|