@signalcore/ui 1.0.0-beta.1 → 1.0.0-beta.3
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/README.md +55 -22
- package/dist/AgentAbout.js +11 -4
- package/dist/AgentAbout.js.map +1 -1
- package/dist/AgentChat.d.ts +7 -1
- package/dist/AgentChat.js +154 -2
- package/dist/AgentChat.js.map +1 -1
- package/dist/AgentSpace.d.ts +12 -3
- package/dist/AgentSpace.js +53 -11
- package/dist/AgentSpace.js.map +1 -1
- package/dist/AgentTasks.js +2 -1
- package/dist/AgentTasks.js.map +1 -1
- package/dist/Threads.d.ts +73 -0
- package/dist/Threads.js +140 -0
- package/dist/Threads.js.map +1 -0
- package/dist/VoiceCall.js +1 -1
- package/dist/VoiceCall.js.map +1 -1
- package/dist/about.d.ts +34 -144
- package/dist/about.js +6 -8
- package/dist/about.js.map +1 -1
- package/dist/automation/cron.d.ts +1 -1
- package/dist/automation/cron.js.map +1 -1
- package/dist/automation/marks.js +13 -5
- package/dist/automation/marks.js.map +1 -1
- package/dist/automation/messages.d.ts +6 -0
- package/dist/automation/messages.js +7 -1
- package/dist/automation/messages.js.map +1 -1
- package/dist/automation/types.d.ts +25 -128
- package/dist/automation/types.js +36 -0
- package/dist/automation/types.js.map +1 -0
- package/dist/automation/when.d.ts +1 -1
- package/dist/automation/when.js +13 -1
- package/dist/automation/when.js.map +1 -1
- package/dist/blocks.d.ts +23 -1
- package/dist/browser.js +2 -2
- package/dist/browser.js.map +1 -1
- package/dist/call.d.ts +1 -1
- package/dist/call.js.map +1 -1
- package/dist/client.d.ts +14 -4
- package/dist/client.js +7 -2
- package/dist/client.js.map +1 -1
- package/dist/contract.d.ts +267 -0
- package/dist/credentials.js.map +1 -1
- package/dist/dev.js +1 -1
- package/dist/dev.js.map +1 -1
- package/dist/es.d.ts +2 -0
- package/dist/es.js +31 -4
- package/dist/es.js.map +1 -1
- package/dist/frames.d.ts +7 -1
- package/dist/frames.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +6 -4
- package/dist/kit/AudioPlayer.js +1 -1
- package/dist/kit/AudioPlayer.js.map +1 -1
- package/dist/kit/Choices.js +1 -1
- package/dist/kit/Choices.js.map +1 -1
- package/dist/kit/FileField.js +1 -1
- package/dist/kit/FileField.js.map +1 -1
- package/dist/kit/Popover.js.map +1 -1
- package/dist/kit/Tile.d.ts +1 -1
- package/dist/kit/Tile.js +1 -1
- package/dist/kit/Tile.js.map +1 -1
- package/dist/kit/messages.d.ts +13 -0
- package/dist/kit/messages.js +7 -1
- package/dist/kit/messages.js.map +1 -1
- package/dist/kit/uploads.d.ts +2 -1
- package/dist/kit/uploads.js +1 -1
- package/dist/kit/uploads.js.map +1 -1
- package/dist/kit/waveform.js.map +1 -1
- package/dist/markdown.js.map +1 -1
- package/dist/marks.js +2 -3
- package/dist/marks.js.map +1 -1
- package/dist/surface/Form.js +1 -1
- package/dist/surface/Markdown.js +1 -1
- package/dist/surface/Markdown.js.map +1 -1
- package/dist/surface/Surface.js +43 -4
- package/dist/surface/Surface.js.map +1 -1
- package/dist/surface/scope.d.ts +1 -1
- package/dist/surface/scope.js.map +1 -1
- package/dist/surface/values.js +1 -1
- package/dist/surface/values.js.map +1 -1
- package/dist/tint.d.ts +1 -1
- package/dist/tint.js +1 -1
- package/dist/tint.js.map +1 -1
- package/dist/types.d.ts +11 -0
- package/dist/types.js.map +1 -1
- package/dist/useConversation.d.ts +2 -0
- package/dist/useConversation.js +16 -0
- package/dist/useConversation.js.map +1 -1
- package/dist/usePolled.js +1 -1
- package/dist/usePolled.js.map +1 -1
- package/dist/voice/webgl.js +1 -1
- package/dist/voice/webgl.js.map +1 -1
- package/dist/widget.js +17 -16
- package/package.json +1 -1
- package/src/AgentAbout.tsx +14 -3
- package/src/AgentChat.tsx +203 -1
- package/src/AgentSpace.tsx +89 -13
- package/src/AgentTasks.tsx +2 -2
- package/src/Threads.tsx +218 -0
- package/src/VoiceCall.tsx +1 -1
- package/src/about.ts +42 -144
- package/src/automation/cron.ts +1 -1
- package/src/automation/marks.tsx +13 -2
- package/src/automation/messages.ts +12 -0
- package/src/automation/types.ts +58 -118
- package/src/automation/when.ts +7 -1
- package/src/blocks.ts +25 -0
- package/src/browser.ts +4 -4
- package/src/call.ts +1 -1
- package/src/client.ts +26 -10
- package/src/contract.ts +1934 -0
- package/src/credentials.ts +2 -2
- package/src/dev.ts +1 -1
- package/src/es.ts +29 -0
- package/src/frames.ts +3 -1
- package/src/index.ts +10 -1
- package/src/kit/AudioPlayer.tsx +1 -1
- package/src/kit/Choices.tsx +1 -1
- package/src/kit/FileField.tsx +1 -1
- package/src/kit/Popover.tsx +1 -1
- package/src/kit/Tile.tsx +1 -1
- package/src/kit/messages.ts +20 -0
- package/src/kit/uploads.ts +2 -2
- package/src/kit/waveform.ts +1 -1
- package/src/markdown.ts +1 -1
- package/src/marks.tsx +3 -123
- package/src/styles.css +206 -0
- package/src/surface/Markdown.tsx +1 -1
- package/src/surface/Surface.tsx +53 -4
- package/src/surface/scope.ts +3 -1
- package/src/surface/values.ts +5 -5
- package/src/tint.ts +1 -1
- package/src/types.ts +11 -0
- package/src/useConversation.ts +10 -0
- package/src/usePolled.ts +2 -2
- package/src/voice/webgl.ts +1 -1
- package/src/widget/element.tsx +13 -4
- package/src/widget/widget.css +13 -0
package/dist/call.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"call.js","names":[],"sources":["../src/call.ts"],"sourcesContent":["/**\n * A voice call with an agent, as the person on it sees it: where it stands, why it ended or\n * never began, and how long it lasted. Pure, so it is tested without a room; `useCall` feeds it\n * what LiveKit says.\n */\n\nimport { ApiError } from \"./client\";\nimport type { CallReason } from \"./types\";\nimport type { OrbState } from \"./voice/orbs\";\n\n/** What the agent is doing, as LiveKit's agents publish it (`lk.agent.state`). */\nexport type AgentActivity = \"initializing\" | \"idle\" | \"listening\" | \"thinking\" | \"speaking\";\n\n/** Why a call that began is over. */\nexport type CallEndedBy =\n /** The person hung up. */\n | \"person\"\n /** The agent did: it said one of its goodbyes. */\n | \"agent\"\n /** It reached its longest time. */\n | \"limit\"\n /** The line went: the network, or the agent's side fell. */\n | \"dropped\";\n\n/** Why a call never began. */\nexport type CallProblem =\n /** The person, or the browser, said no to the microphone. */\n | \"denied\"\n /** There is no microphone to use. */\n | \"nomic\"\n /** Another application holds the microphone. */\n | \"inuse\"\n /** This browser can't call: no microphone access at all (an old one, or a page not on https). */\n | \"unsupported\"\n /** Calls aren't set up where the agent works (the API refused with `no_calls`). */\n | \"off\"\n /** The agent takes no calls: a squad's builder (`builder`). */\n | \"builder\"\n /** The API wouldn't make the call, for any other reason it named. */\n | \"refused\"\n /** The call couldn't be made or joined. */\n | \"failed\"\n /** The room was there, but no agent came to it. */\n | \"unanswered\";\n\n/** How a call ended: why, and how long it lasted from when the agent picked up. */\nexport interface CallEnd {\n /**\n * Why, as far as the room knows: null when the agent's side closed it (its goodbye, or its\n * longest time), which only the API says (`endedByReason`).\n */\n by: CallEndedBy | null;\n seconds: number;\n}\n\n/** Where a call stands. */\nexport interface CallState {\n /** Opening the microphone and the room; ringing until the agent joins; on; over. */\n stage: \"opening\" | \"ringing\" | \"on\" | \"over\";\n /** When the agent joined, in ms; the call's time counts from it. */\n answeredAt: number | null;\n /** What the agent says it is doing; null while it says nothing. */\n agent: AgentActivity | null;\n /** Whether the agent's voice is coming, for an agent that doesn't say what it is doing. */\n speaking: boolean;\n /** The line is being found again; the call goes on once it is. */\n reconnecting: boolean;\n muted: boolean;\n /** The browser holds the agent's voice until the person presses something. */\n silenced: boolean;\n end: CallEnd | null;\n problem: CallProblem | null;\n}\n\nexport const CALL_START: CallState = {\n stage: \"opening\",\n answeredAt: null,\n agent: null,\n speaking: false,\n reconnecting: false,\n muted: false,\n silenced: false,\n end: null,\n problem: null,\n};\n\n/** What moves a call, from the room. */\nexport type CallEvent =\n | { type: \"ringing\" }\n | { type: \"answered\"; at: number }\n | { type: \"agent\"; activity: AgentActivity | null }\n | { type: \"speaking\"; speaking: boolean }\n | { type: \"reconnecting\"; reconnecting: boolean }\n | { type: \"muted\"; muted: boolean }\n | { type: \"silenced\"; silenced: boolean }\n | { type: \"ended\"; by: CallEndedBy | null; at: number }\n | { type: \"failed\"; problem: CallProblem };\n\n/**\n * The call, one event at a time. Once over (ended, or never begun) nothing moves it again: the\n * room's last words after a hang-up (its disconnect, a track going) are not news.\n */\nexport function callReducer(state: CallState, event: CallEvent): CallState {\n if (state.stage === \"over\") return state;\n switch (event.type) {\n case \"ringing\":\n return state.stage === \"opening\" ? { ...state, stage: \"ringing\" } : state;\n case \"answered\":\n return state.answeredAt === null ? { ...state, stage: \"on\", answeredAt: event.at } : state;\n case \"agent\":\n return { ...state, agent: event.activity };\n case \"speaking\":\n return { ...state, speaking: event.speaking };\n case \"reconnecting\":\n return { ...state, reconnecting: event.reconnecting };\n case \"muted\":\n return { ...state, muted: event.muted };\n case \"silenced\":\n return { ...state, silenced: event.silenced };\n case \"ended\": {\n const seconds = state.answeredAt === null ? 0 : secondsBetween(state.answeredAt, event.at);\n return { ...state, stage: \"over\", reconnecting: false, end: { by: event.by, seconds } };\n }\n case \"failed\":\n return { ...state, stage: \"over\", reconnecting: false, problem: event.problem };\n }\n}\n\nconst secondsBetween = (from: number, to: number): number =>\n Math.max(0, Math.floor((to - from) / 1000));\n\n/** What the call shows, in one word: each has its words, its look and its orb. */\nexport type CallLook =\n \"connecting\" | \"listening\" | \"thinking\" | \"speaking\" | \"reconnecting\" | \"ended\" | \"problem\";\n\n/**\n * What the call shows now. Before the agent picks up, and while it starts, it is connecting;\n * then what the agent says it is doing (an agent that says nothing is speaking while its voice\n * comes, listening otherwise).\n */\nexport function lookOf(state: CallState): CallLook {\n if (state.problem !== null) return \"problem\";\n if (state.end !== null) return \"ended\";\n if (state.answeredAt === null || state.agent === \"initializing\") return \"connecting\";\n if (state.reconnecting) return \"reconnecting\";\n if (state.agent === \"thinking\" || state.agent === \"speaking\") return state.agent;\n if (state.agent === null && state.speaking) return \"speaking\";\n return \"listening\";\n}\n\n/**\n * What the orb shows of a call: what the agent is doing once the call is on, moved by whoever is\n * speaking (the agent's voice while it speaks, the person's microphone while it listens); at rest\n * while the line is found (connecting, reconnecting) and once the call is over.\n */\nexport function orbOf(look: CallLook): { state: OrbState; follows: \"agent\" | \"person\" | null } {\n switch (look) {\n case \"speaking\":\n return { state: look, follows: \"agent\" };\n case \"listening\":\n return { state: look, follows: \"person\" };\n case \"thinking\":\n return { state: look, follows: null };\n default:\n return { state: \"idle\", follows: null };\n }\n}\n\n/** The agent's state as LiveKit's agents publish it, or null for anything else. */\nexport function activityOf(value: string | undefined): AgentActivity | null {\n switch (value) {\n case \"initializing\":\n case \"idle\":\n case \"listening\":\n case \"thinking\":\n case \"speaking\":\n return value;\n default:\n return null;\n }\n}\n\n/**\n * Why the room let the person go, as LiveKit says it (`DisconnectReason`'s names): they hung up;\n * the room was closed under them, which is the agent's side ending the call (its goodbye, or its\n * longest time: null, the API says which); anything else, the line went.\n */\nexport function endedBy(reason: string | undefined, hungUp: boolean): CallEndedBy | null {\n if (hungUp || reason === \"CLIENT_INITIATED\") return \"person\";\n if (reason === \"ROOM_DELETED\" || reason === \"ROOM_CLOSED\" || reason === \"PARTICIPANT_REMOVED\") {\n return null;\n }\n return \"dropped\";\n}\n\n/**\n * Why a call ended as the API keeps it (`CallGlance.reason`), as the call says it: the person hung\n * up, the agent said goodbye, it reached its longest time; anything else (it failed, its end never\n * came) is a line that dropped. Null while the API has no end for it yet.\n */\nexport function endedByReason(reason: CallReason | null | undefined): CallEndedBy | null {\n switch (reason) {\n case \"caller\":\n return \"person\";\n case \"agent\":\n return \"agent\";\n case \"max_duration\":\n return \"limit\";\n case null:\n case undefined:\n return null;\n default:\n return \"dropped\";\n }\n}\n\n/**\n * Why a call couldn't be made, from what asking for it threw: the API's refusal, by its code\n * (calls not set up where the agent works, an agent that takes none, any other it named);\n * anything else (no answer, or one that named nothing) is a call that failed.\n */\nexport function refusalOf(error: unknown): CallProblem {\n if (!(error instanceof ApiError) || error.code === null) return \"failed\";\n switch (error.code) {\n case \"no_calls\":\n return \"off\";\n case \"builder\":\n return \"builder\";\n default:\n return \"refused\";\n }\n}\n\n/**\n * Why the microphone couldn't be opened, from the browser's error (`DOMException` names, as\n * LiveKit reads them too); one the browser doesn't name is a call that failed.\n */\nexport function callProblemOf(error: unknown): CallProblem {\n const name = error instanceof Error || isNamed(error) ? error.name : \"\";\n switch (name) {\n case \"NotAllowedError\":\n case \"PermissionDeniedError\":\n case \"SecurityError\":\n return \"denied\";\n case \"NotFoundError\":\n case \"DevicesNotFoundError\":\n case \"OverconstrainedError\":\n return \"nomic\";\n case \"NotReadableError\":\n case \"TrackStartError\":\n case \"AbortError\":\n return \"inuse\";\n default:\n return \"failed\";\n }\n}\n\nconst isNamed = (value: unknown): value is { name: string } =>\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n typeof (value as { name: unknown }).name === \"string\";\n\n/** A call's time as a phone shows it: 0:07, 12:34, 1:02:03. */\nexport function durationOf(seconds: number): string {\n const whole = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(whole / 3600);\n const minutes = Math.floor((whole % 3600) / 60);\n const rest = String(whole % 60).padStart(2, \"0\");\n return hours > 0 ? `${hours}:${String(minutes).padStart(2, \"0\")}:${rest}` : `${minutes}:${rest}`;\n}\n\n/**\n * A loudness read off a track (0 to 1, the root mean square of its spectrum as LiveKit measures\n * it) as the orb shows it: speech sits low on that scale, so it is lifted and the floor of a quiet\n * room cut away.\n */\nexport function loudness(volume: number): number {\n const lifted = (volume - FLOOR) / (1 - FLOOR);\n return Math.min(1, Math.max(0, Math.sqrt(Math.max(0, lifted)) * GAIN));\n}\n\nconst FLOOR = 0.04;\nconst GAIN = 1.25;\n\n/**\n * Whether a key belongs to what it was pressed on, not to the call: a field being typed in, or a\n * control of the page's own (outside the call) that Space presses. Elsewhere (the call itself,\n * the page, a dialog it sits in) Space mutes and unmutes.\n */\nexport function keyBelongsElsewhere(target: EventTarget | null, call: Element | null): boolean {\n if (!(target instanceof Element)) return false;\n if (target.matches(EDITABLE)) return true;\n if (call?.contains(target)) return false;\n return target.matches(PRESSABLE);\n}\n\nconst EDITABLE = 'input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])';\nconst PRESSABLE =\n 'button, a[href], summary, [role=\"button\"], [role=\"checkbox\"], [role=\"switch\"], [role=\"radio\"], [role=\"tab\"], [role=\"menuitem\"], [role=\"option\"], [role=\"slider\"]';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"call.js","names":[],"sources":["../src/call.ts"],"sourcesContent":["/**\n * A voice call with an agent, as the person on it sees it: where it stands, why it ended or\n * never began, and how long it lasted. Pure, so it is tested without a room; `useCall` feeds it\n * what LiveKit says.\n */\n\nimport { ApiError } from \"./client\";\nimport type { CallReason } from \"./types\";\nimport type { OrbState } from \"./voice/orbs\";\n\n/** What the agent is doing, as LiveKit's agents publish it (`lk.agent.state`). */\nexport type AgentActivity = \"initializing\" | \"idle\" | \"listening\" | \"thinking\" | \"speaking\";\n\n/** Why a call that began is over. */\nexport type CallEndedBy =\n /** The person hung up. */\n | \"person\"\n /** The agent did: it said one of its goodbyes. */\n | \"agent\"\n /** It reached its longest time. */\n | \"limit\"\n /** The line went: the network, or the agent's side fell. */\n | \"dropped\";\n\n/** Why a call never began. */\nexport type CallProblem =\n /** The person, or the browser, said no to the microphone. */\n | \"denied\"\n /** There is no microphone to use. */\n | \"nomic\"\n /** Another application holds the microphone. */\n | \"inuse\"\n /** This browser can't call: no microphone access at all (an old one, or a page not on https). */\n | \"unsupported\"\n /** Calls aren't set up where the agent works (the API refused with `no_calls`). */\n | \"off\"\n /** The agent takes no calls: the builder (`builder`). */\n | \"builder\"\n /** The API wouldn't make the call, for any other reason it named. */\n | \"refused\"\n /** The call couldn't be made or joined. */\n | \"failed\"\n /** The room was there, but no agent came to it. */\n | \"unanswered\";\n\n/** How a call ended: why, and how long it lasted from when the agent picked up. */\nexport interface CallEnd {\n /**\n * Why, as far as the room knows: null when the agent's side closed it (its goodbye, or its\n * longest time), which only the API says (`endedByReason`).\n */\n by: CallEndedBy | null;\n seconds: number;\n}\n\n/** Where a call stands. */\nexport interface CallState {\n /** Opening the microphone and the room; ringing until the agent joins; on; over. */\n stage: \"opening\" | \"ringing\" | \"on\" | \"over\";\n /** When the agent joined, in ms; the call's time counts from it. */\n answeredAt: number | null;\n /** What the agent says it is doing; null while it says nothing. */\n agent: AgentActivity | null;\n /** Whether the agent's voice is coming, for an agent that doesn't say what it is doing. */\n speaking: boolean;\n /** The line is being found again; the call goes on once it is. */\n reconnecting: boolean;\n muted: boolean;\n /** The browser holds the agent's voice until the person presses something. */\n silenced: boolean;\n end: CallEnd | null;\n problem: CallProblem | null;\n}\n\nexport const CALL_START: CallState = {\n stage: \"opening\",\n answeredAt: null,\n agent: null,\n speaking: false,\n reconnecting: false,\n muted: false,\n silenced: false,\n end: null,\n problem: null,\n};\n\n/** What moves a call, from the room. */\nexport type CallEvent =\n | { type: \"ringing\" }\n | { type: \"answered\"; at: number }\n | { type: \"agent\"; activity: AgentActivity | null }\n | { type: \"speaking\"; speaking: boolean }\n | { type: \"reconnecting\"; reconnecting: boolean }\n | { type: \"muted\"; muted: boolean }\n | { type: \"silenced\"; silenced: boolean }\n | { type: \"ended\"; by: CallEndedBy | null; at: number }\n | { type: \"failed\"; problem: CallProblem };\n\n/**\n * The call, one event at a time. Once over (ended, or never begun) nothing moves it again: the\n * room's last words after a hang-up (its disconnect, a track going) are not news.\n */\nexport function callReducer(state: CallState, event: CallEvent): CallState {\n if (state.stage === \"over\") return state;\n switch (event.type) {\n case \"ringing\":\n return state.stage === \"opening\" ? { ...state, stage: \"ringing\" } : state;\n case \"answered\":\n return state.answeredAt === null ? { ...state, stage: \"on\", answeredAt: event.at } : state;\n case \"agent\":\n return { ...state, agent: event.activity };\n case \"speaking\":\n return { ...state, speaking: event.speaking };\n case \"reconnecting\":\n return { ...state, reconnecting: event.reconnecting };\n case \"muted\":\n return { ...state, muted: event.muted };\n case \"silenced\":\n return { ...state, silenced: event.silenced };\n case \"ended\": {\n const seconds = state.answeredAt === null ? 0 : secondsBetween(state.answeredAt, event.at);\n return { ...state, stage: \"over\", reconnecting: false, end: { by: event.by, seconds } };\n }\n case \"failed\":\n return { ...state, stage: \"over\", reconnecting: false, problem: event.problem };\n }\n}\n\nconst secondsBetween = (from: number, to: number): number =>\n Math.max(0, Math.floor((to - from) / 1000));\n\n/** What the call shows, in one word: each has its words, its look and its orb. */\nexport type CallLook =\n \"connecting\" | \"listening\" | \"thinking\" | \"speaking\" | \"reconnecting\" | \"ended\" | \"problem\";\n\n/**\n * What the call shows now. Before the agent picks up, and while it starts, it is connecting;\n * then what the agent says it is doing (an agent that says nothing is speaking while its voice\n * comes, listening otherwise).\n */\nexport function lookOf(state: CallState): CallLook {\n if (state.problem !== null) return \"problem\";\n if (state.end !== null) return \"ended\";\n if (state.answeredAt === null || state.agent === \"initializing\") return \"connecting\";\n if (state.reconnecting) return \"reconnecting\";\n if (state.agent === \"thinking\" || state.agent === \"speaking\") return state.agent;\n if (state.agent === null && state.speaking) return \"speaking\";\n return \"listening\";\n}\n\n/**\n * What the orb shows of a call: what the agent is doing once the call is on, moved by whoever is\n * speaking (the agent's voice while it speaks, the person's microphone while it listens); at rest\n * while the line is found (connecting, reconnecting) and once the call is over.\n */\nexport function orbOf(look: CallLook): { state: OrbState; follows: \"agent\" | \"person\" | null } {\n switch (look) {\n case \"speaking\":\n return { state: look, follows: \"agent\" };\n case \"listening\":\n return { state: look, follows: \"person\" };\n case \"thinking\":\n return { state: look, follows: null };\n default:\n return { state: \"idle\", follows: null };\n }\n}\n\n/** The agent's state as LiveKit's agents publish it, or null for anything else. */\nexport function activityOf(value: string | undefined): AgentActivity | null {\n switch (value) {\n case \"initializing\":\n case \"idle\":\n case \"listening\":\n case \"thinking\":\n case \"speaking\":\n return value;\n default:\n return null;\n }\n}\n\n/**\n * Why the room let the person go, as LiveKit says it (`DisconnectReason`'s names): they hung up;\n * the room was closed under them, which is the agent's side ending the call (its goodbye, or its\n * longest time: null, the API says which); anything else, the line went.\n */\nexport function endedBy(reason: string | undefined, hungUp: boolean): CallEndedBy | null {\n if (hungUp || reason === \"CLIENT_INITIATED\") return \"person\";\n if (reason === \"ROOM_DELETED\" || reason === \"ROOM_CLOSED\" || reason === \"PARTICIPANT_REMOVED\") {\n return null;\n }\n return \"dropped\";\n}\n\n/**\n * Why a call ended as the API keeps it (`CallGlance.reason`), as the call says it: the person hung\n * up, the agent said goodbye, it reached its longest time; anything else (it failed, its end never\n * came) is a line that dropped. Null while the API has no end for it yet.\n */\nexport function endedByReason(reason: CallReason | null | undefined): CallEndedBy | null {\n switch (reason) {\n case \"caller\":\n return \"person\";\n case \"agent\":\n return \"agent\";\n case \"max_duration\":\n return \"limit\";\n case null:\n case undefined:\n return null;\n default:\n return \"dropped\";\n }\n}\n\n/**\n * Why a call couldn't be made, from what asking for it threw: the API's refusal, by its code\n * (calls not set up where the agent works, an agent that takes none, any other it named);\n * anything else (no answer, or one that named nothing) is a call that failed.\n */\nexport function refusalOf(error: unknown): CallProblem {\n if (!(error instanceof ApiError) || error.code === null) return \"failed\";\n switch (error.code) {\n case \"no_calls\":\n return \"off\";\n case \"builder\":\n return \"builder\";\n default:\n return \"refused\";\n }\n}\n\n/**\n * Why the microphone couldn't be opened, from the browser's error (`DOMException` names, as\n * LiveKit reads them too); one the browser doesn't name is a call that failed.\n */\nexport function callProblemOf(error: unknown): CallProblem {\n const name = error instanceof Error || isNamed(error) ? error.name : \"\";\n switch (name) {\n case \"NotAllowedError\":\n case \"PermissionDeniedError\":\n case \"SecurityError\":\n return \"denied\";\n case \"NotFoundError\":\n case \"DevicesNotFoundError\":\n case \"OverconstrainedError\":\n return \"nomic\";\n case \"NotReadableError\":\n case \"TrackStartError\":\n case \"AbortError\":\n return \"inuse\";\n default:\n return \"failed\";\n }\n}\n\nconst isNamed = (value: unknown): value is { name: string } =>\n typeof value === \"object\" &&\n value !== null &&\n \"name\" in value &&\n typeof (value as { name: unknown }).name === \"string\";\n\n/** A call's time as a phone shows it: 0:07, 12:34, 1:02:03. */\nexport function durationOf(seconds: number): string {\n const whole = Math.max(0, Math.floor(seconds));\n const hours = Math.floor(whole / 3600);\n const minutes = Math.floor((whole % 3600) / 60);\n const rest = String(whole % 60).padStart(2, \"0\");\n return hours > 0 ? `${hours}:${String(minutes).padStart(2, \"0\")}:${rest}` : `${minutes}:${rest}`;\n}\n\n/**\n * A loudness read off a track (0 to 1, the root mean square of its spectrum as LiveKit measures\n * it) as the orb shows it: speech sits low on that scale, so it is lifted and the floor of a quiet\n * room cut away.\n */\nexport function loudness(volume: number): number {\n const lifted = (volume - FLOOR) / (1 - FLOOR);\n return Math.min(1, Math.max(0, Math.sqrt(Math.max(0, lifted)) * GAIN));\n}\n\nconst FLOOR = 0.04;\nconst GAIN = 1.25;\n\n/**\n * Whether a key belongs to what it was pressed on, not to the call: a field being typed in, or a\n * control of the page's own (outside the call) that Space presses. Elsewhere (the call itself,\n * the page, a dialog it sits in) Space mutes and unmutes.\n */\nexport function keyBelongsElsewhere(target: EventTarget | null, call: Element | null): boolean {\n if (!(target instanceof Element)) return false;\n if (target.matches(EDITABLE)) return true;\n if (call?.contains(target)) return false;\n return target.matches(PRESSABLE);\n}\n\nconst EDITABLE = 'input, textarea, select, [contenteditable]:not([contenteditable=\"false\"])';\nconst PRESSABLE =\n 'button, a[href], summary, [role=\"button\"], [role=\"checkbox\"], [role=\"switch\"], [role=\"radio\"], [role=\"tab\"], [role=\"menuitem\"], [role=\"option\"], [role=\"slider\"]';\n"],"mappings":";;;;;;;AA0EA,MAAa,aAAwB;CACnC,OAAO;CACP,YAAY;CACZ,OAAO;CACP,UAAU;CACV,cAAc;CACd,OAAO;CACP,UAAU;CACV,KAAK;CACL,SAAS;AACX;;;;;AAkBA,SAAgB,YAAY,OAAkB,OAA6B;CACzE,IAAI,MAAM,UAAU,QAAQ,OAAO;CACnC,QAAQ,MAAM,MAAd;EACE,KAAK,WACH,OAAO,MAAM,UAAU,YAAY;GAAE,GAAG;GAAO,OAAO;EAAU,IAAI;EACtE,KAAK,YACH,OAAO,MAAM,eAAe,OAAO;GAAE,GAAG;GAAO,OAAO;GAAM,YAAY,MAAM;EAAG,IAAI;EACvF,KAAK,SACH,OAAO;GAAE,GAAG;GAAO,OAAO,MAAM;EAAS;EAC3C,KAAK,YACH,OAAO;GAAE,GAAG;GAAO,UAAU,MAAM;EAAS;EAC9C,KAAK,gBACH,OAAO;GAAE,GAAG;GAAO,cAAc,MAAM;EAAa;EACtD,KAAK,SACH,OAAO;GAAE,GAAG;GAAO,OAAO,MAAM;EAAM;EACxC,KAAK,YACH,OAAO;GAAE,GAAG;GAAO,UAAU,MAAM;EAAS;EAC9C,KAAK,SAAS;GACZ,MAAM,UAAU,MAAM,eAAe,OAAO,IAAI,eAAe,MAAM,YAAY,MAAM,EAAE;GACzF,OAAO;IAAE,GAAG;IAAO,OAAO;IAAQ,cAAc;IAAO,KAAK;KAAE,IAAI,MAAM;KAAI;IAAQ;GAAE;EACxF;EACA,KAAK,UACH,OAAO;GAAE,GAAG;GAAO,OAAO;GAAQ,cAAc;GAAO,SAAS,MAAM;EAAQ;CAClF;AACF;AAEA,MAAM,kBAAkB,MAAc,OACpC,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,QAAQ,GAAI,CAAC;;;;;;AAW5C,SAAgB,OAAO,OAA4B;CACjD,IAAI,MAAM,YAAY,MAAM,OAAO;CACnC,IAAI,MAAM,QAAQ,MAAM,OAAO;CAC/B,IAAI,MAAM,eAAe,QAAQ,MAAM,UAAU,gBAAgB,OAAO;CACxE,IAAI,MAAM,cAAc,OAAO;CAC/B,IAAI,MAAM,UAAU,cAAc,MAAM,UAAU,YAAY,OAAO,MAAM;CAC3E,IAAI,MAAM,UAAU,QAAQ,MAAM,UAAU,OAAO;CACnD,OAAO;AACT;;;;;;AAOA,SAAgB,MAAM,MAAyE;CAC7F,QAAQ,MAAR;EACE,KAAK,YACH,OAAO;GAAE,OAAO;GAAM,SAAS;EAAQ;EACzC,KAAK,aACH,OAAO;GAAE,OAAO;GAAM,SAAS;EAAS;EAC1C,KAAK,YACH,OAAO;GAAE,OAAO;GAAM,SAAS;EAAK;EACtC,SACE,OAAO;GAAE,OAAO;GAAQ,SAAS;EAAK;CAC1C;AACF;;AAGA,SAAgB,WAAW,OAAiD;CAC1E,QAAQ,OAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK,YACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;;AAOA,SAAgB,QAAQ,QAA4B,QAAqC;CACvF,IAAI,UAAU,WAAW,oBAAoB,OAAO;CACpD,IAAI,WAAW,kBAAkB,WAAW,iBAAiB,WAAW,uBACtE,OAAO;CAET,OAAO;AACT;;;;;;AAOA,SAAgB,cAAc,QAA2D;CACvF,QAAQ,QAAR;EACE,KAAK,UACH,OAAO;EACT,KAAK,SACH,OAAO;EACT,KAAK,gBACH,OAAO;EACT,KAAK;EACL,KAAK,KAAA,GACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;;AAOA,SAAgB,UAAU,OAA6B;CACrD,IAAI,EAAE,iBAAiB,aAAa,MAAM,SAAS,MAAM,OAAO;CAChE,QAAQ,MAAM,MAAd;EACE,KAAK,YACH,OAAO;EACT,KAAK,WACH,OAAO;EACT,SACE,OAAO;CACX;AACF;;;;;AAMA,SAAgB,cAAc,OAA6B;CAEzD,QADa,iBAAiB,SAAS,QAAQ,KAAK,IAAI,MAAM,OAAO,IACrE;EACE,KAAK;EACL,KAAK;EACL,KAAK,iBACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK,wBACH,OAAO;EACT,KAAK;EACL,KAAK;EACL,KAAK,cACH,OAAO;EACT,SACE,OAAO;CACX;AACF;AAEA,MAAM,WAAW,UACf,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACV,OAAQ,MAA4B,SAAS;;AAG/C,SAAgB,WAAW,SAAyB;CAClD,MAAM,QAAQ,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,CAAC;CAC7C,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI;CACrC,MAAM,UAAU,KAAK,MAAO,QAAQ,OAAQ,EAAE;CAC9C,MAAM,OAAO,OAAO,QAAQ,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CAC/C,OAAO,QAAQ,IAAI,GAAG,MAAM,GAAG,OAAO,OAAO,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,SAAS,GAAG,QAAQ,GAAG;AAC5F;;;;;;AAOA,SAAgB,SAAS,QAAwB;CAC/C,MAAM,UAAU,SAAS,UAAU,IAAI;CACvC,OAAO,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,KAAK,KAAK,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI,IAAI,CAAC;AACvE;AAEA,MAAM,QAAQ;AACd,MAAM,OAAO;;;;;;AAOb,SAAgB,oBAAoB,QAA4B,MAA+B;CAC7F,IAAI,EAAE,kBAAkB,UAAU,OAAO;CACzC,IAAI,OAAO,QAAQ,QAAQ,GAAG,OAAO;CACrC,IAAI,MAAM,SAAS,MAAM,GAAG,OAAO;CACnC,OAAO,OAAO,QAAQ,SAAS;AACjC;AAEA,MAAM,WAAW;AACjB,MAAM,YACJ"}
|
package/dist/client.d.ts
CHANGED
|
@@ -74,8 +74,10 @@ export interface Client {
|
|
|
74
74
|
/** Takes one memory from it. */
|
|
75
75
|
forget: (id: string, memoryId: string) => Promise<void>;
|
|
76
76
|
/**
|
|
77
|
-
* The owner's call with the agent, from
|
|
78
|
-
* conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`).
|
|
77
|
+
* The owner's call with the agent, from our own app (v3/VOICE.md): the call and its
|
|
78
|
+
* conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`). App only:
|
|
79
|
+
* it is `/api`'s, signed in by the owner's cookie, and 404s on `/v1`; a host's page calls with
|
|
80
|
+
* `conversations.call(agentId)`.
|
|
79
81
|
*/
|
|
80
82
|
call: (id: string) => Promise<CallToken>;
|
|
81
83
|
};
|
|
@@ -116,8 +118,16 @@ export interface Client {
|
|
|
116
118
|
conversations: {
|
|
117
119
|
/** One conversation with its messages. */
|
|
118
120
|
get: (id: string) => Promise<ConversationDetail>;
|
|
119
|
-
/**
|
|
120
|
-
|
|
121
|
+
/** The agent's conversations, newest first: a person's own only, each with what is unread. */
|
|
122
|
+
list: (agentId: string) => Promise<Conversation[]>;
|
|
123
|
+
/** A new conversation with an agent, titled when given. */
|
|
124
|
+
start: (agentId: string, options?: {
|
|
125
|
+
title?: string | undefined;
|
|
126
|
+
}) => Promise<Conversation>;
|
|
127
|
+
/** How far the person has read it: all of it, now. */
|
|
128
|
+
read: (id: string) => Promise<Conversation>;
|
|
129
|
+
/** Its title, as the person gives it. */
|
|
130
|
+
rename: (id: string, title: string) => Promise<Conversation>;
|
|
121
131
|
/** The files that arrived in a conversation: attached, sent through a form. */
|
|
122
132
|
files: (id: string) => Promise<ConversationFile[]>;
|
|
123
133
|
/**
|
package/dist/client.js
CHANGED
|
@@ -42,6 +42,8 @@ function readJson(text) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
const seg = encodeURIComponent;
|
|
45
|
+
/** A request's JSON body. */
|
|
46
|
+
const body = (value) => new Blob([JSON.stringify(value)], { type: "application/json" });
|
|
45
47
|
/** A file's path in a URL: each name of it encoded, the slashes between them kept. */
|
|
46
48
|
const filePath = (path) => path.split("/").map(seg).join("/");
|
|
47
49
|
/** Where a file's preview is read: under its conversation, its agent or the organization. */
|
|
@@ -175,13 +177,16 @@ function createClient(options) {
|
|
|
175
177
|
runs: { get: (id) => json("GET", `/runs/${seg(id)}`) },
|
|
176
178
|
tasks: {
|
|
177
179
|
list: (agentId) => json("GET", `/agents/${seg(agentId)}/tasks`),
|
|
178
|
-
switch: (agentId, taskId, on) => json("PATCH", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`,
|
|
180
|
+
switch: (agentId, taskId, on) => json("PATCH", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`, body({ on })),
|
|
179
181
|
remove: (agentId, taskId) => run("DELETE", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`)
|
|
180
182
|
},
|
|
181
183
|
channels: { list: (agentId) => json("GET", agentId === void 0 ? "/channels" : `/channels?agent=${seg(agentId)}`) },
|
|
182
184
|
conversations: {
|
|
183
185
|
get: (id) => json("GET", `/conversations/${seg(id)}`),
|
|
184
|
-
|
|
186
|
+
list: (agentId) => json("GET", `/agents/${seg(agentId)}/conversations`),
|
|
187
|
+
start: (agentId, { title } = {}) => json("POST", `/agents/${seg(agentId)}/conversations`, title === void 0 ? void 0 : body({ title })),
|
|
188
|
+
read: (id) => json("POST", `/conversations/${seg(id)}/read`, body({})),
|
|
189
|
+
rename: (id, title) => json("PATCH", `/conversations/${seg(id)}`, body({ title })),
|
|
185
190
|
files: (id) => json("GET", `/conversations/${seg(id)}/files`),
|
|
186
191
|
upload: (id, file, options) => sendFile(`/conversations/${seg(id)}/files`, file, options),
|
|
187
192
|
play: async (id, fileId) => {
|
package/dist/client.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { AboutOut, MemoryFact } from \"./about\";\nimport type { Automation, AutomationView, RunRecord, Task } from \"./automation/types\";\nimport { signerOf, type Credentials, type Speaker } from \"./credentials\";\nimport type {\n AgentFace,\n WidgetConfig,\n CallToken,\n Channel,\n Conversation,\n ConversationDetail,\n ConversationFile,\n FileAt,\n FileLink,\n FilePreview,\n StoredFile,\n} from \"./types\";\n\n/**\n * What the API answered when it was not a success: its status, its sentence (`detail`, written\n * to be shown) and its `code`, for a host to word in its own language.\n */\nexport class ApiError extends Error {\n readonly status: number;\n readonly detail: string;\n /** What went wrong, by name (`not_found`, `no_calls`); null when the answer named nothing. */\n readonly code: string | null;\n\n constructor(status: number, detail: string, code: string | null = null) {\n super(detail);\n this.name = \"ApiError\";\n this.status = status;\n this.detail = detail;\n this.code = code;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isIssue = (value: unknown): value is { msg: string } =>\n isRecord(value) && typeof value.msg === \"string\";\n\n/**\n * Reads an error body: `{code, detail: \"message\"}`, or FastAPI's 422 `{detail: [{msg, …}, …]}`\n * (the first issue speaks for all). A body with neither is named by its status alone.\n */\nexport function parseError(status: number, body: unknown): ApiError {\n const detail = isRecord(body) ? body.detail : undefined;\n const code = isRecord(body) && typeof body.code === \"string\" ? body.code : null;\n if (typeof detail === \"string\" && detail !== \"\") return new ApiError(status, detail, code);\n if (Array.isArray(detail)) {\n const issue = detail.find(isIssue);\n if (issue) return new ApiError(status, issue.msg);\n }\n return new ApiError(status, `HTTP ${status}`);\n}\n\n/** A body read as JSON, or null when it is not. */\nfunction readJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Where the API is, and who calls it (`Credentials`: the cookie, a person's token, a token asked\n * of the host's backend, or a publishable key and its visitor).\n */\nexport type ClientOptions = Credentials & {\n /**\n * The API's origin and prefix: `/api` in our own app (the cookie's, on the page's origin), the\n * public `https://…/v1` from a host's page.\n */\n baseUrl: string;\n /** A host's own `fetch`, when the global one is not the right one. */\n fetch?: typeof fetch | undefined;\n};\n\n/** The files under one owner: the organization's, or an agent's own. */\nexport interface Files {\n /** Every file, by path. */\n list: () => Promise<StoredFile[]>;\n /**\n * Stores the bytes under the path (replacing a file of the same name), with the blob's type\n * as the file's; `searchable` makes it knowledge `search_files` reads (text, PDF).\n */\n put: (path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n delete: (path: string) => Promise<void>;\n}\n\n/** How an upload goes: how much of it has gone, and how to let it go. */\nexport interface UploadOptions {\n /** Hears the share sent so far, from 0 to 1, as it goes. */\n onProgress?: ((share: number) => void) | undefined;\n /** Aborting it stops the upload, and the promise rejects with an `AbortError`. */\n signal?: AbortSignal | undefined;\n}\n\n/** The API, as far as this package reaches it. */\nexport interface Client {\n /**\n * The widget's agent and look, by the publishable key it speaks with (a web channel's key).\n */\n widget: () => Promise<WidgetConfig>;\n /**\n * Who the page speaks for, by its credentials: the business (our own app's cookie) or a person\n * it serves (a token, a publishable key's visitor). What waits for the business's yes is\n * answered by the business alone.\n */\n who: Speaker;\n agents: {\n /** What a person may know of the agent: its name, role, mark, and whether it takes calls. */\n face: (id: string) => Promise<AgentFace>;\n /** What the agent says about itself (null until it has written it) and the facts it rests on. */\n about: (id: string) => Promise<AboutOut>;\n /** Has the agent write about itself anew (a model call, a few seconds); answers like `about`. */\n describe: (id: string) => Promise<AboutOut>;\n /** The agent's own files, by path. */\n files: (id: string) => Promise<StoredFile[]>;\n /** Stores a file of the agent's own; see `Files.put`. */\n putFile: (id: string, path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n deleteFile: (id: string, path: string) => Promise<void>;\n /** What the agent remembered, newest first. */\n memories: (id: string) => Promise<MemoryFact[]>;\n /** Takes one memory from it. */\n forget: (id: string, memoryId: string) => Promise<void>;\n /**\n * The owner's call with the agent, from the browser (v3/VOICE.md): the call and its\n * conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`).\n */\n call: (id: string) => Promise<CallToken>;\n };\n /**\n * The organization's files, shared by every agent; and the preview of any file the caller may\n * read, the organization's, an agent's own or a conversation's (`preview`).\n */\n files: Files & {\n /** What a viewer draws of the file, by its `kind` (v3/FILES.md, Preview). */\n preview: (at: FileAt) => Promise<FilePreview>;\n };\n /** What an agent does on its own (v3/AUTOMATIONS.md). */\n automations: {\n /** The agent's automations, each as it stands now with its newest run. */\n list: (agentId: string) => Promise<Automation[]>;\n /** One of them by its name, with its newest runs. */\n get: (agentId: string, name: string) => Promise<AutomationView>;\n };\n runs: {\n /** One run of an automation with its record: every call it made, step by step. */\n get: (id: string) => Promise<RunRecord>;\n };\n /**\n * The tasks an agent runs for contacts, each with its newest runs: a person's own only (what\n * they asked the agent for in their conversations); the business's, every one.\n */\n tasks: {\n list: (agentId: string) => Promise<Task[]>;\n /** Pauses it, or turns it on again; answers it as it stands. */\n switch: (agentId: string, taskId: string, on: boolean) => Promise<Task>;\n remove: (agentId: string, taskId: string) => Promise<void>;\n };\n /** Where people write to the organization's agents (v3/CHANNELS.md). */\n channels: {\n /** The organization's channels, or one agent's: the disconnected ones too. */\n list: (agentId?: string) => Promise<Channel[]>;\n };\n conversations: {\n /** One conversation with its messages. */\n get: (id: string) => Promise<ConversationDetail>;\n /** A new conversation with an agent. */\n start: (agentId: string) => Promise<Conversation>;\n /** The files that arrived in a conversation: attached, sent through a form. */\n files: (id: string) => Promise<ConversationFile[]>;\n /**\n * Stores a file in the conversation as the person picks it, to say it or send it through a\n * form by its id: its bytes as they are, under its type and name.\n */\n upload: (id: string, file: File, options?: UploadOptions) => Promise<ConversationFile>;\n /**\n * Where a picture, a video or a sound of the conversation plays from in the page's own\n * player: an address that needs no credential, for an hour, streamed as the player seeks.\n */\n play: (id: string, fileId: string) => Promise<string>;\n /**\n * A call with the agent from the browser, for whoever may talk to it (a person, as theirs):\n * the call and its own conversation made now, where what is said lands, and where to join it\n * (`VoiceCall`'s `connect`).\n */\n call: (agentId: string) => Promise<CallToken>;\n };\n /**\n * The conversation's socket address, signed in: `ws:` beside `http:`, `wss:` beside `https:`,\n * with the token (and the visitor) in its query, since a browser's socket takes no header.\n * `fresh` asks the host's backend for a new token first (`getToken`), for a socket that could\n * not open with the last one.\n */\n liveUrl: (conversationId: string, options?: { fresh?: boolean | undefined }) => Promise<string>;\n}\n\nconst seg = encodeURIComponent;\n\n/** A file's path in a URL: each name of it encoded, the slashes between them kept. */\nconst filePath = (path: string): string => path.split(\"/\").map(seg).join(\"/\");\n\n/** Where a file's preview is read: under its conversation, its agent or the organization. */\nconst previewPath = (at: FileAt): string => {\n if (\"conversationId\" in at) {\n return `/conversations/${seg(at.conversationId)}/files/${seg(at.fileId)}/preview`;\n }\n const owner = \"agentId\" in at ? `/agents/${seg(at.agentId)}` : \"\";\n return `${owner}/files/${filePath(at.path)}/preview`;\n};\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/**\n * The API under `baseUrl`: JSON in and out, an `ApiError` for anything but a success, signed in\n * the one way `options` gives. A token asked of the host's backend (`getToken`) that the API\n * refuses (401: it expired) is asked for once more, and the request sent again with it.\n */\nexport function createClient(options: ClientOptions): Client {\n const { baseUrl, fetch: call = fetch } = options;\n const signer = signerOf(options);\n\n /** One request with the cookie or the credential's headers; a body goes as it is, typed. */\n async function attempt(method: Method, path: string, body: Blob | undefined) {\n const signed = await signer.sign();\n const response = await call(`${baseUrl}${path}`, {\n method,\n credentials: signer.credentials,\n headers:\n body === undefined ? signed.headers : { ...signed.headers, \"Content-Type\": body.type },\n ...(body === undefined ? {} : { body }),\n });\n return { response, signed };\n }\n\n async function send(method: Method, path: string, body?: Blob): Promise<Response> {\n let { response, signed } = await attempt(method, path, body);\n if (response.status === 401 && signed.renew !== null) {\n signed.renew();\n ({ response } = await attempt(method, path, body));\n }\n if (!response.ok) {\n const answer: unknown = await response.json().catch(() => null);\n throw parseError(response.status, answer);\n }\n return response;\n }\n\n async function json<T>(method: Method, path: string, body?: Blob): Promise<T> {\n return (await (await send(method, path, body)).json()) as T;\n }\n\n /** A request whose answer has no body (204). */\n async function run(method: Method, path: string): Promise<void> {\n await send(method, path);\n }\n\n /** The four of `Files` under a prefix: `/files`, or `/agents/{id}/files`. */\n const filesUnder = (prefix: string): Files => ({\n list: () => json(\"GET\", prefix),\n put: (path, blob, searchable) =>\n json(\"PUT\", `${prefix}/${filePath(path)}?searchable=${searchable}`, blob),\n delete: (path) => run(\"DELETE\", `${prefix}/${filePath(path)}`),\n });\n\n const own = (id: string) => filesUnder(`/agents/${seg(id)}/files`);\n\n /**\n * A file sent by XMLHttpRequest, the one way a browser tells how much of a body has gone; the\n * cookie goes as with `fetch` on the same origin, any other credential as its headers. Refused\n * with a token asked of the host's backend, it is sent once more with a new one.\n */\n async function sendFile(\n path: string,\n file: File,\n options: UploadOptions = {},\n ): Promise<ConversationFile> {\n const signed = await signer.sign();\n try {\n return await post(path, file, signed.headers, options);\n } catch (error) {\n const expired = error instanceof ApiError && error.status === 401;\n if (!expired || signed.renew === null) throw error;\n signed.renew();\n return post(path, file, (await signer.sign()).headers, options);\n }\n }\n\n function post(\n path: string,\n file: File,\n headers: Readonly<Record<string, string>>,\n { onProgress, signal }: UploadOptions,\n ): Promise<ConversationFile> {\n return new Promise((resolve, reject) => {\n const aborted = () => new DOMException(\"The upload was aborted\", \"AbortError\");\n if (signal?.aborted) {\n reject(aborted());\n return;\n }\n const request = new XMLHttpRequest();\n const abort = () => {\n request.abort();\n };\n request.open(\"POST\", `${baseUrl}${path}`);\n for (const [name, value] of Object.entries(headers)) request.setRequestHeader(name, value);\n request.setRequestHeader(\"Content-Type\", file.type || \"application/octet-stream\");\n request.setRequestHeader(\"X-File-Name\", encodeURIComponent(file.name));\n request.upload.addEventListener(\"progress\", (event) => {\n if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);\n });\n request.addEventListener(\"loadend\", () => {\n signal?.removeEventListener(\"abort\", abort);\n });\n request.addEventListener(\"load\", () => {\n const body = readJson(request.responseText);\n if (request.status >= 200 && request.status < 300) resolve(body as ConversationFile);\n else reject(parseError(request.status, body));\n });\n request.addEventListener(\"error\", () => {\n reject(new ApiError(0, \"The upload could not reach the server\"));\n });\n request.addEventListener(\"abort\", () => {\n reject(aborted());\n });\n signal?.addEventListener(\"abort\", abort, { once: true });\n request.send(file);\n });\n }\n\n return {\n widget: () => json(\"GET\", \"/channels/web/widget\"),\n who: signer.who,\n agents: {\n face: (id) => json(\"GET\", `/agents/${seg(id)}/face`),\n about: (id) => json(\"GET\", `/agents/${seg(id)}/about`),\n describe: (id) => json(\"POST\", `/agents/${seg(id)}/describe`),\n files: (id) => own(id).list(),\n putFile: (id, path, blob, searchable) => own(id).put(path, blob, searchable),\n deleteFile: (id, path) => own(id).delete(path),\n memories: (id) => json(\"GET\", `/agents/${seg(id)}/memories`),\n forget: (id, memoryId) => run(\"DELETE\", `/agents/${seg(id)}/memories/${seg(memoryId)}`),\n call: (id) => json(\"POST\", `/agents/${seg(id)}/voice/token`),\n },\n files: { ...filesUnder(\"/files\"), preview: (at) => json(\"GET\", previewPath(at)) },\n automations: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/automations`),\n get: (agentId, name) => json(\"GET\", `/agents/${seg(agentId)}/automations/${seg(name)}`),\n },\n runs: {\n get: (id) => json(\"GET\", `/runs/${seg(id)}`),\n },\n tasks: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/tasks`),\n switch: (agentId, taskId, on) =>\n json(\n \"PATCH\",\n `/agents/${seg(agentId)}/tasks/${seg(taskId)}`,\n new Blob([JSON.stringify({ on })], { type: \"application/json\" }),\n ),\n remove: (agentId, taskId) => run(\"DELETE\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`),\n },\n channels: {\n list: (agentId) =>\n json(\"GET\", agentId === undefined ? \"/channels\" : `/channels?agent=${seg(agentId)}`),\n },\n conversations: {\n get: (id) => json(\"GET\", `/conversations/${seg(id)}`),\n start: (agentId) => json(\"POST\", `/agents/${seg(agentId)}/conversations`),\n files: (id) => json(\"GET\", `/conversations/${seg(id)}/files`),\n upload: (id, file, options) => sendFile(`/conversations/${seg(id)}/files`, file, options),\n play: async (id, fileId) => {\n const link = await json<FileLink>(\n \"GET\",\n `/conversations/${seg(id)}/files/${seg(fileId)}/link`,\n );\n return new URL(`${baseUrl}${link.path}`, window.location.href).href;\n },\n call: (agentId) => json(\"POST\", `/agents/${seg(agentId)}/calls`),\n },\n liveUrl: async (conversationId, { fresh = false } = {}) => {\n const url = new URL(\n `${baseUrl}/conversations/${seg(conversationId)}/live`,\n window.location.href,\n );\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n for (const [name, value] of Object.entries(await signer.query(fresh))) {\n url.searchParams.set(name, value);\n }\n return url.href;\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { AboutOut, MemoryFact } from \"./about\";\nimport type { Automation, AutomationView, RunRecord, Task } from \"./automation/types\";\nimport { signerOf, type Credentials, type Speaker } from \"./credentials\";\nimport type {\n AgentFace,\n WidgetConfig,\n CallToken,\n Channel,\n Conversation,\n ConversationDetail,\n ConversationFile,\n FileAt,\n FileLink,\n FilePreview,\n StoredFile,\n} from \"./types\";\n\n/**\n * What the API answered when it was not a success: its status, its sentence (`detail`, written\n * to be shown) and its `code`, for a host to word in its own language.\n */\nexport class ApiError extends Error {\n readonly status: number;\n readonly detail: string;\n /** What went wrong, by name (`not_found`, `no_calls`); null when the answer named nothing. */\n readonly code: string | null;\n\n constructor(status: number, detail: string, code: string | null = null) {\n super(detail);\n this.name = \"ApiError\";\n this.status = status;\n this.detail = detail;\n this.code = code;\n }\n}\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst isIssue = (value: unknown): value is { msg: string } =>\n isRecord(value) && typeof value.msg === \"string\";\n\n/**\n * Reads an error body: `{code, detail: \"message\"}`, or FastAPI's 422 `{detail: [{msg, …}, …]}`\n * (the first issue speaks for all). A body with neither is named by its status alone.\n */\nexport function parseError(status: number, body: unknown): ApiError {\n const detail = isRecord(body) ? body.detail : undefined;\n const code = isRecord(body) && typeof body.code === \"string\" ? body.code : null;\n if (typeof detail === \"string\" && detail !== \"\") return new ApiError(status, detail, code);\n if (Array.isArray(detail)) {\n const issue = detail.find(isIssue);\n if (issue) return new ApiError(status, issue.msg);\n }\n return new ApiError(status, `HTTP ${status}`);\n}\n\n/** A body read as JSON, or null when it is not. */\nfunction readJson(text: string): unknown {\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return null;\n }\n}\n\n/**\n * Where the API is, and who calls it (`Credentials`: the cookie, a person's token, a token asked\n * of the host's backend, or a publishable key and its visitor).\n */\nexport type ClientOptions = Credentials & {\n /**\n * The API's origin and prefix: `/api` in our own app (the cookie's, on the page's origin), the\n * public `https://…/v1` from a host's page.\n */\n baseUrl: string;\n /** A host's own `fetch`, when the global one is not the right one. */\n fetch?: typeof fetch | undefined;\n};\n\n/** The files under one owner: the organization's, or an agent's own. */\nexport interface Files {\n /** Every file, by path. */\n list: () => Promise<StoredFile[]>;\n /**\n * Stores the bytes under the path (replacing a file of the same name), with the blob's type\n * as the file's; `searchable` makes it knowledge `search_files` reads (text, PDF).\n */\n put: (path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n delete: (path: string) => Promise<void>;\n}\n\n/** How an upload goes: how much of it has gone, and how to let it go. */\nexport interface UploadOptions {\n /** Hears the share sent so far, from 0 to 1, as it goes. */\n onProgress?: ((share: number) => void) | undefined;\n /** Aborting it stops the upload, and the promise rejects with an `AbortError`. */\n signal?: AbortSignal | undefined;\n}\n\n/** The API, as far as this package reaches it. */\nexport interface Client {\n /**\n * The widget's agent and look, by the publishable key it speaks with (a web channel's key).\n */\n widget: () => Promise<WidgetConfig>;\n /**\n * Who the page speaks for, by its credentials: the business (our own app's cookie) or a person\n * it serves (a token, a publishable key's visitor). What waits for the business's yes is\n * answered by the business alone.\n */\n who: Speaker;\n agents: {\n /** What a person may know of the agent: its name, role, mark, and whether it takes calls. */\n face: (id: string) => Promise<AgentFace>;\n /** What the agent says about itself (null until it has written it) and the facts it rests on. */\n about: (id: string) => Promise<AboutOut>;\n /** Has the agent write about itself anew (a model call, a few seconds); answers like `about`. */\n describe: (id: string) => Promise<AboutOut>;\n /** The agent's own files, by path. */\n files: (id: string) => Promise<StoredFile[]>;\n /** Stores a file of the agent's own; see `Files.put`. */\n putFile: (id: string, path: string, blob: Blob, searchable: boolean) => Promise<StoredFile>;\n deleteFile: (id: string, path: string) => Promise<void>;\n /** What the agent remembered, newest first. */\n memories: (id: string) => Promise<MemoryFact[]>;\n /** Takes one memory from it. */\n forget: (id: string, memoryId: string) => Promise<void>;\n /**\n * The owner's call with the agent, from our own app (v3/VOICE.md): the call and its\n * conversation (a test) made now, and where to join it (`VoiceCall`'s `connect`). App only:\n * it is `/api`'s, signed in by the owner's cookie, and 404s on `/v1`; a host's page calls with\n * `conversations.call(agentId)`.\n */\n call: (id: string) => Promise<CallToken>;\n };\n /**\n * The organization's files, shared by every agent; and the preview of any file the caller may\n * read, the organization's, an agent's own or a conversation's (`preview`).\n */\n files: Files & {\n /** What a viewer draws of the file, by its `kind` (v3/FILES.md, Preview). */\n preview: (at: FileAt) => Promise<FilePreview>;\n };\n /** What an agent does on its own (v3/AUTOMATIONS.md). */\n automations: {\n /** The agent's automations, each as it stands now with its newest run. */\n list: (agentId: string) => Promise<Automation[]>;\n /** One of them by its name, with its newest runs. */\n get: (agentId: string, name: string) => Promise<AutomationView>;\n };\n runs: {\n /** One run of an automation with its record: every call it made, step by step. */\n get: (id: string) => Promise<RunRecord>;\n };\n /**\n * The tasks an agent runs for contacts, each with its newest runs: a person's own only (what\n * they asked the agent for in their conversations); the business's, every one.\n */\n tasks: {\n list: (agentId: string) => Promise<Task[]>;\n /** Pauses it, or turns it on again; answers it as it stands. */\n switch: (agentId: string, taskId: string, on: boolean) => Promise<Task>;\n remove: (agentId: string, taskId: string) => Promise<void>;\n };\n /** Where people write to the organization's agents (v3/CHANNELS.md). */\n channels: {\n /** The organization's channels, or one agent's: the disconnected ones too. */\n list: (agentId?: string) => Promise<Channel[]>;\n };\n conversations: {\n /** One conversation with its messages. */\n get: (id: string) => Promise<ConversationDetail>;\n /** The agent's conversations, newest first: a person's own only, each with what is unread. */\n list: (agentId: string) => Promise<Conversation[]>;\n /** A new conversation with an agent, titled when given. */\n start: (agentId: string, options?: { title?: string | undefined }) => Promise<Conversation>;\n /** How far the person has read it: all of it, now. */\n read: (id: string) => Promise<Conversation>;\n /** Its title, as the person gives it. */\n rename: (id: string, title: string) => Promise<Conversation>;\n /** The files that arrived in a conversation: attached, sent through a form. */\n files: (id: string) => Promise<ConversationFile[]>;\n /**\n * Stores a file in the conversation as the person picks it, to say it or send it through a\n * form by its id: its bytes as they are, under its type and name.\n */\n upload: (id: string, file: File, options?: UploadOptions) => Promise<ConversationFile>;\n /**\n * Where a picture, a video or a sound of the conversation plays from in the page's own\n * player: an address that needs no credential, for an hour, streamed as the player seeks.\n */\n play: (id: string, fileId: string) => Promise<string>;\n /**\n * A call with the agent from the browser, for whoever may talk to it (a person, as theirs):\n * the call and its own conversation made now, where what is said lands, and where to join it\n * (`VoiceCall`'s `connect`).\n */\n call: (agentId: string) => Promise<CallToken>;\n };\n /**\n * The conversation's socket address, signed in: `ws:` beside `http:`, `wss:` beside `https:`,\n * with the token (and the visitor) in its query, since a browser's socket takes no header.\n * `fresh` asks the host's backend for a new token first (`getToken`), for a socket that could\n * not open with the last one.\n */\n liveUrl: (conversationId: string, options?: { fresh?: boolean | undefined }) => Promise<string>;\n}\n\nconst seg = encodeURIComponent;\n\n/** A request's JSON body. */\nconst body = (value: unknown): Blob =>\n new Blob([JSON.stringify(value)], { type: \"application/json\" });\n\n/** A file's path in a URL: each name of it encoded, the slashes between them kept. */\nconst filePath = (path: string): string => path.split(\"/\").map(seg).join(\"/\");\n\n/** Where a file's preview is read: under its conversation, its agent or the organization. */\nconst previewPath = (at: FileAt): string => {\n if (\"conversationId\" in at) {\n return `/conversations/${seg(at.conversationId)}/files/${seg(at.fileId)}/preview`;\n }\n const owner = \"agentId\" in at ? `/agents/${seg(at.agentId)}` : \"\";\n return `${owner}/files/${filePath(at.path)}/preview`;\n};\n\ntype Method = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\n/**\n * The API under `baseUrl`: JSON in and out, an `ApiError` for anything but a success, signed in\n * the one way `options` gives. A token asked of the host's backend (`getToken`) that the API\n * refuses (401: it expired) is asked for once more, and the request sent again with it.\n */\nexport function createClient(options: ClientOptions): Client {\n const { baseUrl, fetch: call = fetch } = options;\n const signer = signerOf(options);\n\n /** One request with the cookie or the credential's headers; a body goes as it is, typed. */\n async function attempt(method: Method, path: string, body: Blob | undefined) {\n const signed = await signer.sign();\n const response = await call(`${baseUrl}${path}`, {\n method,\n credentials: signer.credentials,\n headers:\n body === undefined ? signed.headers : { ...signed.headers, \"Content-Type\": body.type },\n ...(body === undefined ? {} : { body }),\n });\n return { response, signed };\n }\n\n async function send(method: Method, path: string, body?: Blob): Promise<Response> {\n let { response, signed } = await attempt(method, path, body);\n if (response.status === 401 && signed.renew !== null) {\n signed.renew();\n ({ response } = await attempt(method, path, body));\n }\n if (!response.ok) {\n const answer: unknown = await response.json().catch(() => null);\n throw parseError(response.status, answer);\n }\n return response;\n }\n\n async function json<T>(method: Method, path: string, body?: Blob): Promise<T> {\n return (await (await send(method, path, body)).json()) as T;\n }\n\n /** A request whose answer has no body (204). */\n async function run(method: Method, path: string): Promise<void> {\n await send(method, path);\n }\n\n /** The four of `Files` under a prefix: `/files`, or `/agents/{id}/files`. */\n const filesUnder = (prefix: string): Files => ({\n list: () => json(\"GET\", prefix),\n put: (path, blob, searchable) =>\n json(\"PUT\", `${prefix}/${filePath(path)}?searchable=${searchable}`, blob),\n delete: (path) => run(\"DELETE\", `${prefix}/${filePath(path)}`),\n });\n\n const own = (id: string) => filesUnder(`/agents/${seg(id)}/files`);\n\n /**\n * A file sent by XMLHttpRequest, the one way a browser tells how much of a body has gone; the\n * cookie goes as with `fetch` on the same origin, any other credential as its headers. Refused\n * with a token asked of the host's backend, it is sent once more with a new one.\n */\n async function sendFile(\n path: string,\n file: File,\n options: UploadOptions = {},\n ): Promise<ConversationFile> {\n const signed = await signer.sign();\n try {\n return await post(path, file, signed.headers, options);\n } catch (error) {\n const expired = error instanceof ApiError && error.status === 401;\n if (!expired || signed.renew === null) throw error;\n signed.renew();\n return post(path, file, (await signer.sign()).headers, options);\n }\n }\n\n function post(\n path: string,\n file: File,\n headers: Readonly<Record<string, string>>,\n { onProgress, signal }: UploadOptions,\n ): Promise<ConversationFile> {\n return new Promise((resolve, reject) => {\n const aborted = () => new DOMException(\"The upload was aborted\", \"AbortError\");\n if (signal?.aborted) {\n reject(aborted());\n return;\n }\n const request = new XMLHttpRequest();\n const abort = () => {\n request.abort();\n };\n request.open(\"POST\", `${baseUrl}${path}`);\n for (const [name, value] of Object.entries(headers)) request.setRequestHeader(name, value);\n request.setRequestHeader(\"Content-Type\", file.type || \"application/octet-stream\");\n request.setRequestHeader(\"X-File-Name\", encodeURIComponent(file.name));\n request.upload.addEventListener(\"progress\", (event) => {\n if (event.lengthComputable && event.total > 0) onProgress?.(event.loaded / event.total);\n });\n request.addEventListener(\"loadend\", () => {\n signal?.removeEventListener(\"abort\", abort);\n });\n request.addEventListener(\"load\", () => {\n const body = readJson(request.responseText);\n if (request.status >= 200 && request.status < 300) resolve(body as ConversationFile);\n else reject(parseError(request.status, body));\n });\n request.addEventListener(\"error\", () => {\n reject(new ApiError(0, \"The upload could not reach the server\"));\n });\n request.addEventListener(\"abort\", () => {\n reject(aborted());\n });\n signal?.addEventListener(\"abort\", abort, { once: true });\n request.send(file);\n });\n }\n\n return {\n widget: () => json(\"GET\", \"/channels/web/widget\"),\n who: signer.who,\n agents: {\n face: (id) => json(\"GET\", `/agents/${seg(id)}/face`),\n about: (id) => json(\"GET\", `/agents/${seg(id)}/about`),\n describe: (id) => json(\"POST\", `/agents/${seg(id)}/describe`),\n files: (id) => own(id).list(),\n putFile: (id, path, blob, searchable) => own(id).put(path, blob, searchable),\n deleteFile: (id, path) => own(id).delete(path),\n memories: (id) => json(\"GET\", `/agents/${seg(id)}/memories`),\n forget: (id, memoryId) => run(\"DELETE\", `/agents/${seg(id)}/memories/${seg(memoryId)}`),\n call: (id) => json(\"POST\", `/agents/${seg(id)}/voice/token`),\n },\n files: { ...filesUnder(\"/files\"), preview: (at) => json(\"GET\", previewPath(at)) },\n automations: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/automations`),\n get: (agentId, name) => json(\"GET\", `/agents/${seg(agentId)}/automations/${seg(name)}`),\n },\n runs: {\n get: (id) => json(\"GET\", `/runs/${seg(id)}`),\n },\n tasks: {\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/tasks`),\n switch: (agentId, taskId, on) =>\n json(\"PATCH\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`, body({ on })),\n remove: (agentId, taskId) => run(\"DELETE\", `/agents/${seg(agentId)}/tasks/${seg(taskId)}`),\n },\n channels: {\n list: (agentId) =>\n json(\"GET\", agentId === undefined ? \"/channels\" : `/channels?agent=${seg(agentId)}`),\n },\n conversations: {\n get: (id) => json(\"GET\", `/conversations/${seg(id)}`),\n list: (agentId) => json(\"GET\", `/agents/${seg(agentId)}/conversations`),\n start: (agentId, { title } = {}) =>\n json(\n \"POST\",\n `/agents/${seg(agentId)}/conversations`,\n title === undefined ? undefined : body({ title }),\n ),\n read: (id) => json(\"POST\", `/conversations/${seg(id)}/read`, body({})),\n rename: (id, title) => json(\"PATCH\", `/conversations/${seg(id)}`, body({ title })),\n files: (id) => json(\"GET\", `/conversations/${seg(id)}/files`),\n upload: (id, file, options) => sendFile(`/conversations/${seg(id)}/files`, file, options),\n play: async (id, fileId) => {\n const link = await json<FileLink>(\n \"GET\",\n `/conversations/${seg(id)}/files/${seg(fileId)}/link`,\n );\n return new URL(`${baseUrl}${link.path}`, window.location.href).href;\n },\n call: (agentId) => json(\"POST\", `/agents/${seg(agentId)}/calls`),\n },\n liveUrl: async (conversationId, { fresh = false } = {}) => {\n const url = new URL(\n `${baseUrl}/conversations/${seg(conversationId)}/live`,\n window.location.href,\n );\n url.protocol = url.protocol === \"https:\" ? \"wss:\" : \"ws:\";\n for (const [name, value] of Object.entries(await signer.query(fresh))) {\n url.searchParams.set(name, value);\n }\n return url.href;\n },\n };\n}\n"],"mappings":";;;;;;AAqBA,IAAa,WAAb,cAA8B,MAAM;CAClC;CACA;;CAEA;CAEA,YAAY,QAAgB,QAAgB,OAAsB,MAAM;EACtE,MAAM,MAAM;EACZ,KAAK,OAAO;EACZ,KAAK,SAAS;EACd,KAAK,SAAS;EACd,KAAK,OAAO;CACd;AACF;AAEA,MAAM,YAAY,UAChB,OAAO,UAAU,YAAY,UAAU;AAEzC,MAAM,WAAW,UACf,SAAS,KAAK,KAAK,OAAO,MAAM,QAAQ;;;;;AAM1C,SAAgB,WAAW,QAAgB,MAAyB;CAClE,MAAM,SAAS,SAAS,IAAI,IAAI,KAAK,SAAS,KAAA;CAC9C,MAAM,OAAO,SAAS,IAAI,KAAK,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO;CAC3E,IAAI,OAAO,WAAW,YAAY,WAAW,IAAI,OAAO,IAAI,SAAS,QAAQ,QAAQ,IAAI;CACzF,IAAI,MAAM,QAAQ,MAAM,GAAG;EACzB,MAAM,QAAQ,OAAO,KAAK,OAAO;EACjC,IAAI,OAAO,OAAO,IAAI,SAAS,QAAQ,MAAM,GAAG;CAClD;CACA,OAAO,IAAI,SAAS,QAAQ,QAAQ,QAAQ;AAC9C;;AAGA,SAAS,SAAS,MAAuB;CACvC,IAAI;EACF,OAAO,KAAK,MAAM,IAAI;CACxB,QAAQ;EACN,OAAO;CACT;AACF;AAiJA,MAAM,MAAM;;AAGZ,MAAM,QAAQ,UACZ,IAAI,KAAK,CAAC,KAAK,UAAU,KAAK,CAAC,GAAG,EAAE,MAAM,mBAAmB,CAAC;;AAGhE,MAAM,YAAY,SAAyB,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG;;AAG5E,MAAM,eAAe,OAAuB;CAC1C,IAAI,oBAAoB,IACtB,OAAO,kBAAkB,IAAI,GAAG,cAAc,EAAE,SAAS,IAAI,GAAG,MAAM,EAAE;CAG1E,OAAO,GADO,aAAa,KAAK,WAAW,IAAI,GAAG,OAAO,MAAM,GAC/C,SAAS,SAAS,GAAG,IAAI,EAAE;AAC7C;;;;;;AASA,SAAgB,aAAa,SAAgC;CAC3D,MAAM,EAAE,SAAS,OAAO,OAAO,UAAU;CACzC,MAAM,SAAS,SAAS,OAAO;;CAG/B,eAAe,QAAQ,QAAgB,MAAc,MAAwB;EAC3E,MAAM,SAAS,MAAM,OAAO,KAAK;EAQjC,OAAO;GAAE,UAAA,MAPc,KAAK,GAAG,UAAU,QAAQ;IAC/C;IACA,aAAa,OAAO;IACpB,SACE,SAAS,KAAA,IAAY,OAAO,UAAU;KAAE,GAAG,OAAO;KAAS,gBAAgB,KAAK;IAAK;IACvF,GAAI,SAAS,KAAA,IAAY,CAAC,IAAI,EAAE,KAAK;GACvC,CAAC;GACkB;EAAO;CAC5B;CAEA,eAAe,KAAK,QAAgB,MAAc,MAAgC;EAChF,IAAI,EAAE,UAAU,WAAW,MAAM,QAAQ,QAAQ,MAAM,IAAI;EAC3D,IAAI,SAAS,WAAW,OAAO,OAAO,UAAU,MAAM;GACpD,OAAO,MAAM;GACb,CAAC,CAAE,YAAa,MAAM,QAAQ,QAAQ,MAAM,IAAI;EAClD;EACA,IAAI,CAAC,SAAS,IAAI;GAChB,MAAM,SAAkB,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI;GAC9D,MAAM,WAAW,SAAS,QAAQ,MAAM;EAC1C;EACA,OAAO;CACT;CAEA,eAAe,KAAQ,QAAgB,MAAc,MAAyB;EAC5E,OAAQ,OAAO,MAAM,KAAK,QAAQ,MAAM,IAAI,EAAA,CAAG,KAAK;CACtD;;CAGA,eAAe,IAAI,QAAgB,MAA6B;EAC9D,MAAM,KAAK,QAAQ,IAAI;CACzB;;CAGA,MAAM,cAAc,YAA2B;EAC7C,YAAY,KAAK,OAAO,MAAM;EAC9B,MAAM,MAAM,MAAM,eAChB,KAAK,OAAO,GAAG,OAAO,GAAG,SAAS,IAAI,EAAE,cAAc,cAAc,IAAI;EAC1E,SAAS,SAAS,IAAI,UAAU,GAAG,OAAO,GAAG,SAAS,IAAI,GAAG;CAC/D;CAEA,MAAM,OAAO,OAAe,WAAW,WAAW,IAAI,EAAE,EAAE,OAAO;;;;;;CAOjE,eAAe,SACb,MACA,MACA,UAAyB,CAAC,GACC;EAC3B,MAAM,SAAS,MAAM,OAAO,KAAK;EACjC,IAAI;GACF,OAAO,MAAM,KAAK,MAAM,MAAM,OAAO,SAAS,OAAO;EACvD,SAAS,OAAO;GAEd,IAAI,EADY,iBAAiB,YAAY,MAAM,WAAW,QAC9C,OAAO,UAAU,MAAM,MAAM;GAC7C,OAAO,MAAM;GACb,OAAO,KAAK,MAAM,OAAO,MAAM,OAAO,KAAK,EAAA,CAAG,SAAS,OAAO;EAChE;CACF;CAEA,SAAS,KACP,MACA,MACA,SACA,EAAE,YAAY,UACa;EAC3B,OAAO,IAAI,SAAS,SAAS,WAAW;GACtC,MAAM,gBAAgB,IAAI,aAAa,0BAA0B,YAAY;GAC7E,IAAI,QAAQ,SAAS;IACnB,OAAO,QAAQ,CAAC;IAChB;GACF;GACA,MAAM,UAAU,IAAI,eAAe;GACnC,MAAM,cAAc;IAClB,QAAQ,MAAM;GAChB;GACA,QAAQ,KAAK,QAAQ,GAAG,UAAU,MAAM;GACxC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,OAAO,GAAG,QAAQ,iBAAiB,MAAM,KAAK;GACzF,QAAQ,iBAAiB,gBAAgB,KAAK,QAAQ,0BAA0B;GAChF,QAAQ,iBAAiB,eAAe,mBAAmB,KAAK,IAAI,CAAC;GACrE,QAAQ,OAAO,iBAAiB,aAAa,UAAU;IACrD,IAAI,MAAM,oBAAoB,MAAM,QAAQ,GAAG,aAAa,MAAM,SAAS,MAAM,KAAK;GACxF,CAAC;GACD,QAAQ,iBAAiB,iBAAiB;IACxC,QAAQ,oBAAoB,SAAS,KAAK;GAC5C,CAAC;GACD,QAAQ,iBAAiB,cAAc;IACrC,MAAM,OAAO,SAAS,QAAQ,YAAY;IAC1C,IAAI,QAAQ,UAAU,OAAO,QAAQ,SAAS,KAAK,QAAQ,IAAwB;SAC9E,OAAO,WAAW,QAAQ,QAAQ,IAAI,CAAC;GAC9C,CAAC;GACD,QAAQ,iBAAiB,eAAe;IACtC,OAAO,IAAI,SAAS,GAAG,uCAAuC,CAAC;GACjE,CAAC;GACD,QAAQ,iBAAiB,eAAe;IACtC,OAAO,QAAQ,CAAC;GAClB,CAAC;GACD,QAAQ,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;GACvD,QAAQ,KAAK,IAAI;EACnB,CAAC;CACH;CAEA,OAAO;EACL,cAAc,KAAK,OAAO,sBAAsB;EAChD,KAAK,OAAO;EACZ,QAAQ;GACN,OAAO,OAAO,KAAK,OAAO,WAAW,IAAI,EAAE,EAAE,MAAM;GACnD,QAAQ,OAAO,KAAK,OAAO,WAAW,IAAI,EAAE,EAAE,OAAO;GACrD,WAAW,OAAO,KAAK,QAAQ,WAAW,IAAI,EAAE,EAAE,UAAU;GAC5D,QAAQ,OAAO,IAAI,EAAE,CAAC,CAAC,KAAK;GAC5B,UAAU,IAAI,MAAM,MAAM,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,MAAM,MAAM,UAAU;GAC3E,aAAa,IAAI,SAAS,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI;GAC7C,WAAW,OAAO,KAAK,OAAO,WAAW,IAAI,EAAE,EAAE,UAAU;GAC3D,SAAS,IAAI,aAAa,IAAI,UAAU,WAAW,IAAI,EAAE,EAAE,YAAY,IAAI,QAAQ,GAAG;GACtF,OAAO,OAAO,KAAK,QAAQ,WAAW,IAAI,EAAE,EAAE,aAAa;EAC7D;EACA,OAAO;GAAE,GAAG,WAAW,QAAQ;GAAG,UAAU,OAAO,KAAK,OAAO,YAAY,EAAE,CAAC;EAAE;EAChF,aAAa;GACX,OAAO,YAAY,KAAK,OAAO,WAAW,IAAI,OAAO,EAAE,aAAa;GACpE,MAAM,SAAS,SAAS,KAAK,OAAO,WAAW,IAAI,OAAO,EAAE,eAAe,IAAI,IAAI,GAAG;EACxF;EACA,MAAM,EACJ,MAAM,OAAO,KAAK,OAAO,SAAS,IAAI,EAAE,GAAG,EAC7C;EACA,OAAO;GACL,OAAO,YAAY,KAAK,OAAO,WAAW,IAAI,OAAO,EAAE,OAAO;GAC9D,SAAS,SAAS,QAAQ,OACxB,KAAK,SAAS,WAAW,IAAI,OAAO,EAAE,SAAS,IAAI,MAAM,KAAK,KAAK,EAAE,GAAG,CAAC,CAAC;GAC5E,SAAS,SAAS,WAAW,IAAI,UAAU,WAAW,IAAI,OAAO,EAAE,SAAS,IAAI,MAAM,GAAG;EAC3F;EACA,UAAU,EACR,OAAO,YACL,KAAK,OAAO,YAAY,KAAA,IAAY,cAAc,mBAAmB,IAAI,OAAO,GAAG,EACvF;EACA,eAAe;GACb,MAAM,OAAO,KAAK,OAAO,kBAAkB,IAAI,EAAE,GAAG;GACpD,OAAO,YAAY,KAAK,OAAO,WAAW,IAAI,OAAO,EAAE,eAAe;GACtE,QAAQ,SAAS,EAAE,UAAU,CAAC,MAC5B,KACE,QACA,WAAW,IAAI,OAAO,EAAE,iBACxB,UAAU,KAAA,IAAY,KAAA,IAAY,KAAK,EAAE,MAAM,CAAC,CAClD;GACF,OAAO,OAAO,KAAK,QAAQ,kBAAkB,IAAI,EAAE,EAAE,QAAQ,KAAK,CAAC,CAAC,CAAC;GACrE,SAAS,IAAI,UAAU,KAAK,SAAS,kBAAkB,IAAI,EAAE,KAAK,KAAK,EAAE,MAAM,CAAC,CAAC;GACjF,QAAQ,OAAO,KAAK,OAAO,kBAAkB,IAAI,EAAE,EAAE,OAAO;GAC5D,SAAS,IAAI,MAAM,YAAY,SAAS,kBAAkB,IAAI,EAAE,EAAE,SAAS,MAAM,OAAO;GACxF,MAAM,OAAO,IAAI,WAAW;IAC1B,MAAM,OAAO,MAAM,KACjB,OACA,kBAAkB,IAAI,EAAE,EAAE,SAAS,IAAI,MAAM,EAAE,MACjD;IACA,OAAO,IAAI,IAAI,GAAG,UAAU,KAAK,QAAQ,OAAO,SAAS,IAAI,CAAC,CAAC;GACjE;GACA,OAAO,YAAY,KAAK,QAAQ,WAAW,IAAI,OAAO,EAAE,OAAO;EACjE;EACA,SAAS,OAAO,gBAAgB,EAAE,QAAQ,UAAU,CAAC,MAAM;GACzD,MAAM,MAAM,IAAI,IACd,GAAG,QAAQ,iBAAiB,IAAI,cAAc,EAAE,QAChD,OAAO,SAAS,IAClB;GACA,IAAI,WAAW,IAAI,aAAa,WAAW,SAAS;GACpD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,MAAM,OAAO,MAAM,KAAK,CAAC,GAClE,IAAI,aAAa,IAAI,MAAM,KAAK;GAElC,OAAO,IAAI;EACb;CACF;AACF"}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
//#region src/contract.d.ts
|
|
2
|
+
/** What the agent says about itself. */
|
|
3
|
+
export interface About {
|
|
4
|
+
summary: string;
|
|
5
|
+
does: Array<Does>;
|
|
6
|
+
asks: Array<string>;
|
|
7
|
+
icon?: "sparkle" | "calendar" | "headset" | "message" | "phone" | "mail" | "cart" | "card" | "chart" | "megaphone" | "wrench" | "briefcase" | "heart" | "user" | "search" | "file" | "globe" | "code" | "map" | null;
|
|
8
|
+
}
|
|
9
|
+
/** Qué puede: the agent's own words (None until written) and the facts they rest on. */
|
|
10
|
+
export interface AboutOut {
|
|
11
|
+
about: About | null;
|
|
12
|
+
about_at: string | null;
|
|
13
|
+
facts: Facts;
|
|
14
|
+
}
|
|
15
|
+
/** One action as an agent has it: where it comes from, whether it is on and asks first. */
|
|
16
|
+
export interface ActionRowOut {
|
|
17
|
+
name: string;
|
|
18
|
+
description: string;
|
|
19
|
+
destructive: boolean;
|
|
20
|
+
input_schema?: Record<string, unknown>;
|
|
21
|
+
source: "default" | "library" | "own";
|
|
22
|
+
on: boolean;
|
|
23
|
+
needs_approval: boolean;
|
|
24
|
+
}
|
|
25
|
+
export interface AutomationDetailOut {
|
|
26
|
+
id: string;
|
|
27
|
+
agent_id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
title: string;
|
|
30
|
+
summary: string;
|
|
31
|
+
trigger: TriggerView;
|
|
32
|
+
on: boolean;
|
|
33
|
+
next_run_at: string | null;
|
|
34
|
+
steps: Array<Step>;
|
|
35
|
+
board: Board | null;
|
|
36
|
+
state: "active" | "paused" | "failing";
|
|
37
|
+
last_run: RunOut | null;
|
|
38
|
+
runs: Array<RunOut>;
|
|
39
|
+
}
|
|
40
|
+
export interface AutomationOut {
|
|
41
|
+
id: string;
|
|
42
|
+
agent_id: string;
|
|
43
|
+
name: string;
|
|
44
|
+
title: string;
|
|
45
|
+
summary: string;
|
|
46
|
+
trigger: TriggerView;
|
|
47
|
+
on: boolean;
|
|
48
|
+
next_run_at: string | null;
|
|
49
|
+
steps: Array<Step>;
|
|
50
|
+
board: Board | null;
|
|
51
|
+
state: "active" | "paused" | "failing";
|
|
52
|
+
last_run: RunOut | null;
|
|
53
|
+
}
|
|
54
|
+
/** What the automation moves: its table's rows counted by the value of one field. */
|
|
55
|
+
export interface Board {
|
|
56
|
+
table: string;
|
|
57
|
+
title: string;
|
|
58
|
+
field: string;
|
|
59
|
+
counts: Array<Count>;
|
|
60
|
+
}
|
|
61
|
+
export interface Count {
|
|
62
|
+
value: string;
|
|
63
|
+
count: number;
|
|
64
|
+
}
|
|
65
|
+
/** One automation, as the owner reads it. */
|
|
66
|
+
export interface Does {
|
|
67
|
+
automation: string;
|
|
68
|
+
text: string;
|
|
69
|
+
when: string;
|
|
70
|
+
}
|
|
71
|
+
export interface FactAsk {
|
|
72
|
+
agent_id: string;
|
|
73
|
+
name: string;
|
|
74
|
+
}
|
|
75
|
+
/** An automation; one on an app's event also with its app's provider, name and mark, which the others leave out, as they always have. */
|
|
76
|
+
export interface FactAutomation {
|
|
77
|
+
id: string;
|
|
78
|
+
name: string;
|
|
79
|
+
kind: "schedule" | "webhook" | "event" | "platform" | "manual" | "after";
|
|
80
|
+
title: string;
|
|
81
|
+
summary: string;
|
|
82
|
+
schedule: string | null;
|
|
83
|
+
timezone: string | null;
|
|
84
|
+
app: string | null;
|
|
85
|
+
event: string | null;
|
|
86
|
+
config: Record<string, unknown>;
|
|
87
|
+
url: string | null;
|
|
88
|
+
say: string;
|
|
89
|
+
on: boolean;
|
|
90
|
+
last_run_at: string | null;
|
|
91
|
+
next_run_at: string | null;
|
|
92
|
+
provider?: string | null;
|
|
93
|
+
app_title?: string | null;
|
|
94
|
+
icon?: string | null;
|
|
95
|
+
}
|
|
96
|
+
export interface FactChannel {
|
|
97
|
+
kind: "whatsapp" | "instagram" | "sms" | "phone" | "web";
|
|
98
|
+
name: string;
|
|
99
|
+
status: string;
|
|
100
|
+
}
|
|
101
|
+
/** An app it was given: the app's name and mark, and its tools, with their names for people (`titles`) and as the model calls them (`called`: `gmail__Gmail_SendEmail`), each by the tool's own. */
|
|
102
|
+
export interface FactConnection {
|
|
103
|
+
name: string;
|
|
104
|
+
provider: "composio" | "arcade";
|
|
105
|
+
toolkit: string;
|
|
106
|
+
title: string;
|
|
107
|
+
status: string;
|
|
108
|
+
actions: Array<string>;
|
|
109
|
+
titles: Record<string, string>;
|
|
110
|
+
called: Record<string, string>;
|
|
111
|
+
icon: string | null;
|
|
112
|
+
}
|
|
113
|
+
export interface FactDevice {
|
|
114
|
+
kind: "extension" | "installer";
|
|
115
|
+
name: string;
|
|
116
|
+
status: string;
|
|
117
|
+
}
|
|
118
|
+
export interface FactFile {
|
|
119
|
+
path: string;
|
|
120
|
+
size: number;
|
|
121
|
+
searchable: boolean;
|
|
122
|
+
shared: boolean;
|
|
123
|
+
}
|
|
124
|
+
export interface FactKey {
|
|
125
|
+
name: string;
|
|
126
|
+
hosts: Array<string>;
|
|
127
|
+
shared: boolean;
|
|
128
|
+
}
|
|
129
|
+
export interface FactTable {
|
|
130
|
+
table: string;
|
|
131
|
+
can: Array<string>;
|
|
132
|
+
title: string | null;
|
|
133
|
+
}
|
|
134
|
+
/** What the agent has, as the app draws it. Every list is here from day one; the ones whose phase hasn't come are empty, and their shape is fixed so the renderer never changes. */
|
|
135
|
+
export interface Facts {
|
|
136
|
+
actions: Array<ActionRowOut>;
|
|
137
|
+
automations: Array<FactAutomation>;
|
|
138
|
+
connections: Array<FactConnection>;
|
|
139
|
+
channels: Array<FactChannel>;
|
|
140
|
+
files: Array<FactFile>;
|
|
141
|
+
memories: Array<MemoryOut>;
|
|
142
|
+
vault: Array<FactKey>;
|
|
143
|
+
data: Array<FactTable>;
|
|
144
|
+
asks: Array<FactAsk>;
|
|
145
|
+
devices: Array<FactDevice>;
|
|
146
|
+
}
|
|
147
|
+
export interface MemoryOut {
|
|
148
|
+
id: string;
|
|
149
|
+
agent_id: string;
|
|
150
|
+
contact_id?: string | null;
|
|
151
|
+
text: string;
|
|
152
|
+
created_at: string;
|
|
153
|
+
}
|
|
154
|
+
export interface RunDetailOut {
|
|
155
|
+
id: string;
|
|
156
|
+
agent_id: string;
|
|
157
|
+
automation: string;
|
|
158
|
+
title: string;
|
|
159
|
+
by: "schedule" | "webhook" | "event" | "platform" | "agent" | "test" | "api" | "after";
|
|
160
|
+
conversation_id: string | null;
|
|
161
|
+
status: "running" | "waiting" | "done" | "failed" | "skipped";
|
|
162
|
+
summary: string | null;
|
|
163
|
+
error: string | null;
|
|
164
|
+
dry: boolean;
|
|
165
|
+
started_at: string;
|
|
166
|
+
ended_at: string | null;
|
|
167
|
+
tokens_in: number;
|
|
168
|
+
tokens_out: number;
|
|
169
|
+
opened: Array<string>;
|
|
170
|
+
decision?: RunsDecisionOut | null;
|
|
171
|
+
result?: unknown;
|
|
172
|
+
contact_id?: string | null;
|
|
173
|
+
steps: Array<RunStepOut>;
|
|
174
|
+
}
|
|
175
|
+
export interface RunOut {
|
|
176
|
+
id: string;
|
|
177
|
+
agent_id: string;
|
|
178
|
+
automation: string;
|
|
179
|
+
title: string;
|
|
180
|
+
by: "schedule" | "webhook" | "event" | "platform" | "agent" | "test" | "api" | "after";
|
|
181
|
+
conversation_id: string | null;
|
|
182
|
+
status: "running" | "waiting" | "done" | "failed" | "skipped";
|
|
183
|
+
summary: string | null;
|
|
184
|
+
error: string | null;
|
|
185
|
+
dry: boolean;
|
|
186
|
+
started_at: string;
|
|
187
|
+
ended_at: string | null;
|
|
188
|
+
tokens_in: number;
|
|
189
|
+
tokens_out: number;
|
|
190
|
+
opened: Array<string>;
|
|
191
|
+
decision?: RunsDecisionOut | null;
|
|
192
|
+
result?: unknown;
|
|
193
|
+
contact_id?: string | null;
|
|
194
|
+
}
|
|
195
|
+
export interface RunStepOut {
|
|
196
|
+
seq: number;
|
|
197
|
+
step: string | null;
|
|
198
|
+
call: string;
|
|
199
|
+
input: unknown;
|
|
200
|
+
output: unknown;
|
|
201
|
+
error: string | null;
|
|
202
|
+
ms: number;
|
|
203
|
+
at: string;
|
|
204
|
+
would: boolean;
|
|
205
|
+
}
|
|
206
|
+
/** What a handler decided of a message (`sc.answer`, `sc.hold`, `sc.ignore`). */
|
|
207
|
+
export interface RunsDecisionOut {
|
|
208
|
+
kind: "answer" | "hold" | "ignore";
|
|
209
|
+
reason: string;
|
|
210
|
+
}
|
|
211
|
+
export interface Step {
|
|
212
|
+
name: string;
|
|
213
|
+
uses: Array<Use>;
|
|
214
|
+
}
|
|
215
|
+
/** A contact's subscription: an instance of a definition (`automation`: the business's automation for contacts, by name; None for an instruction), what it gives it (`inputs`: an instruction's words are `inputs.instruction`), when (`schedule`, a cron line in `timezone`, or once at `next_run_at`), whether it is on, its contact, the conversation it runs about and its newest runs. It read as an automation before: those fields stay, deprecated. */
|
|
216
|
+
export interface TaskOut {
|
|
217
|
+
id: string;
|
|
218
|
+
agent_id: string;
|
|
219
|
+
automation: string | null;
|
|
220
|
+
title: string;
|
|
221
|
+
inputs: Record<string, unknown>;
|
|
222
|
+
schedule: string | null;
|
|
223
|
+
timezone: string | null;
|
|
224
|
+
on: boolean;
|
|
225
|
+
next_run_at: string | null;
|
|
226
|
+
last_run_at: string | null;
|
|
227
|
+
contact_id: string;
|
|
228
|
+
conversation_id: string | null;
|
|
229
|
+
runs: Array<RunOut>;
|
|
230
|
+
created_at: string;
|
|
231
|
+
/** @deprecated Kept for the clients that read it. */
|
|
232
|
+
name: string;
|
|
233
|
+
/** @deprecated Kept for the clients that read it. */
|
|
234
|
+
summary: string;
|
|
235
|
+
/** @deprecated Kept for the clients that read it. */
|
|
236
|
+
trigger: TriggerView;
|
|
237
|
+
/** @deprecated Kept for the clients that read it. */
|
|
238
|
+
steps: Array<Step>;
|
|
239
|
+
/** @deprecated Kept for the clients that read it. */
|
|
240
|
+
board: Board | null;
|
|
241
|
+
/** @deprecated Kept for the clients that read it. */
|
|
242
|
+
state: "active" | "paused" | "failing";
|
|
243
|
+
/** @deprecated Kept for the clients that read it. */
|
|
244
|
+
last_run: RunOut | null;
|
|
245
|
+
}
|
|
246
|
+
export interface TriggerView {
|
|
247
|
+
kind: "schedule" | "webhook" | "event" | "platform" | "manual" | "after";
|
|
248
|
+
schedule: string | null;
|
|
249
|
+
timezone: string | null;
|
|
250
|
+
url: string | null;
|
|
251
|
+
app: string | null;
|
|
252
|
+
app_title: string | null;
|
|
253
|
+
event: string | null;
|
|
254
|
+
event_title: string | null;
|
|
255
|
+
icon: string | null;
|
|
256
|
+
decides?: boolean;
|
|
257
|
+
channels?: Array<string>;
|
|
258
|
+
after?: string | null;
|
|
259
|
+
}
|
|
260
|
+
export interface Use {
|
|
261
|
+
kind: "app" | "ai" | "agent" | "notify" | "send" | "data" | "http";
|
|
262
|
+
app: string | null;
|
|
263
|
+
title: string | null;
|
|
264
|
+
icon: string | null;
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//# sourceMappingURL=contract.d.ts.map
|
package/dist/credentials.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"credentials.js","names":[],"sources":["../src/credentials.ts"],"sourcesContent":["/**\n * Who a client calls as, one way at a time (v3/README.md, Credentials): the owner's session cookie\n * in our own app; a person's token, given as it is or asked of the host's backend (and asked again\n * once it expires); or an anonymous visitor of a site, by the site's publishable key and the id\n * the page keeps for them.\n */\n\ntype Way = \"credentials\" | \"token\" | \"getToken\" | \"publishableKey\" | \"visitorId\";\n\n/** One way of signing in, with none of the others beside it. */\ntype Alone<T> = T & { [K in Exclude<Way, keyof T>]?: never };\n\nexport type Credentials =\n /** Our app, on the API's own origin (`/api`): the session cookie the browser holds. */\n | Alone<{ credentials: \"cookie\" }>\n /** A person's token the host's backend minted (`POST /v1/tokens`), used as it is. */\n | Alone<{ token: string }>\n /**\n * A person's token asked of the host's backend: when first needed, and once more each time the\n * API refuses the one kept (it lasts an hour).\n */\n | Alone<{ getToken: () => Promise<string> }>\n /**\n * An anonymous visitor (the widget): the site's publishable key, and who they are, an id the\n * page keeps; one is made and kept in `localStorage` when none is given.\n */\n | Alone<{ publishableKey: string; visitorId?: string | undefined }>;\n\n/** What one request carries to say who calls, and how to have it anew once it is refused. */\
|
|
1
|
+
{"version":3,"file":"credentials.js","names":[],"sources":["../src/credentials.ts"],"sourcesContent":["/**\n * Who a client calls as, one way at a time (v3/README.md, Credentials): the owner's session cookie\n * in our own app; a person's token, given as it is or asked of the host's backend (and asked again\n * once it expires); or an anonymous visitor of a site, by the site's publishable key and the id\n * the page keeps for them.\n */\n\ntype Way = \"credentials\" | \"token\" | \"getToken\" | \"publishableKey\" | \"visitorId\";\n\n/** One way of signing in, with none of the others beside it. */\ntype Alone<T> = T & { [K in Exclude<Way, keyof T>]?: never };\n\nexport type Credentials =\n /** Our app, on the API's own origin (`/api`): the session cookie the browser holds. */\n | Alone<{ credentials: \"cookie\" }>\n /** A person's token the host's backend minted (`POST /v1/tokens`), used as it is. */\n | Alone<{ token: string }>\n /**\n * A person's token asked of the host's backend: when first needed, and once more each time the\n * API refuses the one kept (it lasts an hour).\n */\n | Alone<{ getToken: () => Promise<string> }>\n /**\n * An anonymous visitor (the widget): the site's publishable key, and who they are, an id the\n * page keeps; one is made and kept in `localStorage` when none is given.\n */\n | Alone<{ publishableKey: string; visitorId?: string | undefined }>;\n\n/** What one request carries to say who calls, and how to have it anew once it is refused. */\ninterface Signed {\n headers: Readonly<Record<string, string>>;\n /** Lets go of a token the API refused, for the next request to ask for another; null: none. */\n renew: (() => void) | null;\n}\n\n/**\n * Who a page speaks for: the business (the owner, in our own app) or a person it serves (a token's\n * person, a publishable key's visitor). The API lets each answer different things: a yes the\n * business decides is the business's alone.\n */\nexport type Speaker = \"business\" | \"person\";\n\n/** How every request and socket of a client says who calls. */\ninterface Signer {\n who: Speaker;\n /** What `fetch` sends of the browser's own: the cookie on the page's origin, or nothing. */\n credentials: RequestCredentials;\n sign: () => Promise<Signed>;\n /**\n * What a socket carries in its address instead (a browser's socket takes no header); `fresh`\n * asks for a new token first, for a socket that could not open with the last one.\n */\n query: (fresh: boolean) => Promise<Readonly<Record<string, string>>>;\n}\n\n/** Where the page keeps its visitor's id, for every SignalCore widget on the site. */\nexport const VISITOR_KEY = \"signalcore.visitor\";\n\n/** What the API takes as a visitor's id: 16 to 128 letters, digits, `_` and `-`. */\nconst VISITOR = /^[A-Za-z0-9_-]{16,128}$/;\n\n/** A new visitor's id: 32 hex characters of the browser's own randomness (any page has it). */\nfunction newVisitor(): string {\n const bytes = crypto.getRandomValues(new Uint8Array(16));\n return Array.from(bytes, (byte) => byte.toString(16).padStart(2, \"0\")).join(\"\");\n}\n\n/**\n * The visitor this page speaks for: the one kept in `localStorage`, else a new one kept there for\n * their next visit; where the page can't keep anything (a private window, storage blocked), a new\n * one for this page alone.\n */\nexport function keptVisitor(): string {\n try {\n const kept = localStorage.getItem(VISITOR_KEY);\n if (kept !== null && VISITOR.test(kept)) return kept;\n const made = newVisitor();\n localStorage.setItem(VISITOR_KEY, made);\n return made;\n } catch {\n return newVisitor();\n }\n}\n\n/** A credential that never changes: the same headers and the same query, nothing to renew. */\nconst fixed = (\n who: Speaker,\n credentials: RequestCredentials,\n headers: Record<string, string>,\n query: Record<string, string>,\n): Signer => ({\n who,\n credentials,\n sign: () => Promise.resolve({ headers, renew: null }),\n query: () => Promise.resolve(query),\n});\n\n/**\n * A token asked of the host's backend: when first needed, then kept. A refusal lets it go, once\n * however many requests were refused with it, so the next request asks for another; one that\n * could not be had is asked for again next time.\n */\nfunction fetched(getToken: () => Promise<string>): Signer {\n let current: Promise<string> | null = null;\n const latest = (): Promise<string> => {\n if (current !== null) return current;\n const asked = Promise.resolve().then(getToken);\n current = asked;\n asked.catch(() => {\n if (current === asked) current = null;\n });\n return asked;\n };\n return {\n who: \"person\",\n credentials: \"omit\",\n sign: async () => {\n const used = latest();\n const token = await used;\n return {\n headers: { Authorization: `Bearer ${token}` },\n renew: () => {\n if (current === used) current = null;\n },\n };\n },\n query: async (fresh) => {\n if (fresh) current = null;\n return { token: await latest() };\n },\n };\n}\n\n/**\n * How a client signs in with what it was given. The API's public prefix reads the header (the\n * socket, its query) and never a cookie, so only our own app sends the browser's.\n */\nexport function signerOf(given: Credentials): Signer {\n if (given.credentials === \"cookie\") return fixed(\"business\", \"same-origin\", {}, {});\n if (typeof given.token === \"string\") {\n const { token } = given;\n return fixed(\"person\", \"omit\", { Authorization: `Bearer ${token}` }, { token });\n }\n if (typeof given.getToken === \"function\") return fetched(given.getToken);\n if (typeof given.publishableKey === \"string\") {\n const key = given.publishableKey;\n const visitor = given.visitorId ?? keptVisitor();\n return fixed(\n \"person\",\n \"omit\",\n { Authorization: `Bearer ${key}`, \"SignalCore-Visitor\": visitor },\n { token: key, visitor },\n );\n }\n throw new TypeError(\n 'createClient takes one way to sign in: credentials: \"cookie\", token, getToken or publishableKey',\n );\n}\n"],"mappings":";;AAwDA,MAAa,cAAc;;AAG3B,MAAM,UAAU;;AAGhB,SAAS,aAAqB;CAC5B,MAAM,QAAQ,OAAO,gCAAgB,IAAI,WAAW,EAAE,CAAC;CACvD,OAAO,MAAM,KAAK,QAAQ,SAAS,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE;AAChF;;;;;;AAOA,SAAgB,cAAsB;CACpC,IAAI;EACF,MAAM,OAAO,aAAa,QAAQ,WAAW;EAC7C,IAAI,SAAS,QAAQ,QAAQ,KAAK,IAAI,GAAG,OAAO;EAChD,MAAM,OAAO,WAAW;EACxB,aAAa,QAAQ,aAAa,IAAI;EACtC,OAAO;CACT,QAAQ;EACN,OAAO,WAAW;CACpB;AACF;;AAGA,MAAM,SACJ,KACA,aACA,SACA,WACY;CACZ;CACA;CACA,YAAY,QAAQ,QAAQ;EAAE;EAAS,OAAO;CAAK,CAAC;CACpD,aAAa,QAAQ,QAAQ,KAAK;AACpC;;;;;;AAOA,SAAS,QAAQ,UAAyC;CACxD,IAAI,UAAkC;CACtC,MAAM,eAAgC;EACpC,IAAI,YAAY,MAAM,OAAO;EAC7B,MAAM,QAAQ,QAAQ,QAAQ,CAAC,CAAC,KAAK,QAAQ;EAC7C,UAAU;EACV,MAAM,YAAY;GAChB,IAAI,YAAY,OAAO,UAAU;EACnC,CAAC;EACD,OAAO;CACT;CACA,OAAO;EACL,KAAK;EACL,aAAa;EACb,MAAM,YAAY;GAChB,MAAM,OAAO,OAAO;GAEpB,OAAO;IACL,SAAS,EAAE,eAAe,UAAU,MAFlB,OAE0B;IAC5C,aAAa;KACX,IAAI,YAAY,MAAM,UAAU;IAClC;GACF;EACF;EACA,OAAO,OAAO,UAAU;GACtB,IAAI,OAAO,UAAU;GACrB,OAAO,EAAE,OAAO,MAAM,OAAO,EAAE;EACjC;CACF;AACF;;;;;AAMA,SAAgB,SAAS,OAA4B;CACnD,IAAI,MAAM,gBAAgB,UAAU,OAAO,MAAM,YAAY,eAAe,CAAC,GAAG,CAAC,CAAC;CAClF,IAAI,OAAO,MAAM,UAAU,UAAU;EACnC,MAAM,EAAE,UAAU;EAClB,OAAO,MAAM,UAAU,QAAQ,EAAE,eAAe,UAAU,QAAQ,GAAG,EAAE,MAAM,CAAC;CAChF;CACA,IAAI,OAAO,MAAM,aAAa,YAAY,OAAO,QAAQ,MAAM,QAAQ;CACvE,IAAI,OAAO,MAAM,mBAAmB,UAAU;EAC5C,MAAM,MAAM,MAAM;EAClB,MAAM,UAAU,MAAM,aAAa,YAAY;EAC/C,OAAO,MACL,UACA,QACA;GAAE,eAAe,UAAU;GAAO,sBAAsB;EAAQ,GAChE;GAAE,OAAO;GAAK;EAAQ,CACxB;CACF;CACA,MAAM,IAAI,UACR,mGACF;AACF"}
|
package/dist/dev.js
CHANGED
package/dist/dev.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.js","names":[],"sources":["../src/dev.ts"],"sourcesContent":["/**\n * Whether the page runs a development build, as React tells it: a host's bundler writes in\n * `process.env.NODE_ENV`; a page without one has no `process`, and counts as production.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction development(): boolean {\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\
|
|
1
|
+
{"version":3,"file":"dev.js","names":[],"sources":["../src/dev.ts"],"sourcesContent":["/**\n * Whether the page runs a development build, as React tells it: a host's bundler writes in\n * `process.env.NODE_ENV`; a page without one has no `process`, and counts as production.\n */\ndeclare const process: { env: { NODE_ENV?: string } };\n\nfunction development(): boolean {\n try {\n return process.env.NODE_ENV !== \"production\";\n } catch {\n return false;\n }\n}\n\nconst DEV = development();\n\n/** A note for the developer of the page, in development only: what the package let pass, and why. */\nexport function note(...said: unknown[]): void {\n if (DEV) console.debug(\"[signalcore]\", ...said);\n}\n"],"mappings":";AAMA,SAAS,cAAuB;CAC9B,IAAI;EACF,OAAA,QAAA,IAAA,aAAgC;CAClC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,MAAM,MAAM,YAAY;;AAGxB,SAAgB,KAAK,GAAG,MAAuB;CAC7C,IAAI,KAAK,QAAQ,MAAM,gBAAgB,GAAG,IAAI;AAChD"}
|
package/dist/es.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { KitMessages } from "./kit/messages.js";
|
|
|
3
3
|
import { TaskMessages } from "./AgentTasks.js";
|
|
4
4
|
import { BrowserMessages } from "./BrowserView.js";
|
|
5
5
|
import { ChatMessages } from "./AgentChat.js";
|
|
6
|
+
import { ThreadMessages } from "./Threads.js";
|
|
6
7
|
import { VoiceCallMessages } from "./VoiceCall.js";
|
|
7
8
|
import { SpaceMessages } from "./AgentSpace.js";
|
|
8
9
|
//#region src/es.d.ts
|
|
@@ -12,6 +13,7 @@ export declare const BROWSER_MESSAGES_ES: BrowserMessages;
|
|
|
12
13
|
export declare const CHAT_MESSAGES_ES: ChatMessages;
|
|
13
14
|
export declare const VOICE_CALL_MESSAGES_ES: VoiceCallMessages;
|
|
14
15
|
export declare const TASK_MESSAGES_ES: TaskMessages;
|
|
16
|
+
export declare const THREAD_MESSAGES_ES: ThreadMessages;
|
|
15
17
|
export declare const SPACE_MESSAGES_ES: SpaceMessages;
|
|
16
18
|
//#endregion
|
|
17
19
|
//# sourceMappingURL=es.d.ts.map
|