pinecall-protocol 0.1.0__tar.gz → 0.3.0__tar.gz
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.
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/.gitignore +3 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/PKG-INFO +1 -1
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/_version.py +1 -1
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/events.py +6 -2
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/fixtures/call-log-golden.json +1 -1
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/rest.py +373 -2
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/.python-version +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/LICENSE +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/README.md +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/__init__.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/_base.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/codec.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/commands.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/defs.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/envelope.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/fixtures/__init__.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/fixtures/call-log-golden.state.json +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/metrics.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/py.typed +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/registry.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/room.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/state.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/verbs.py +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pyproject.toml +0 -0
- {pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/uv.lock +0 -0
|
@@ -60,9 +60,12 @@ class AgentStateChanged(WireModel):
|
|
|
60
60
|
state: AgentState
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
#
|
|
63
|
+
# One delta of the reply the agent is giving, never the reply so far: in a voice call one word, as
|
|
64
|
+
# the voice plays it, with the seconds it was aligned to; in a written call one model token. The
|
|
65
|
+
# reply so far is every delta of the same speech_id since the last turn.agent, joined; turn.agent
|
|
66
|
+
# carries the whole reply and closes it. Interim entries are ephemeral.
|
|
64
67
|
class AgentTranscript(WireModel):
|
|
65
|
-
"""
|
|
68
|
+
"""One delta of the reply the agent is giving, never the reply so far."""
|
|
66
69
|
|
|
67
70
|
speech_id: str
|
|
68
71
|
text: str
|
|
@@ -81,6 +84,7 @@ class CallDialing(WireModel):
|
|
|
81
84
|
run: str | None = None
|
|
82
85
|
caller: Contact | None
|
|
83
86
|
external_id: str | None = None
|
|
87
|
+
asked_by: str | None = None
|
|
84
88
|
|
|
85
89
|
|
|
86
90
|
# Nothing about the conversation follows; call.summary still does.
|
{pinecall_protocol-0.1.0 → pinecall_protocol-0.3.0}/pinecall_protocol/fixtures/call-log-golden.json
RENAMED
|
@@ -122,6 +122,6 @@
|
|
|
122
122
|
{"seq": 122, "ts": 1786537552.4, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "call.ended", "ephemeral": false, "data": {"reason": "caller_hung_up", "ended_by": "caller", "ended_at": 1786537552.4, "duration_s": 51.988}},
|
|
123
123
|
{"seq": 123, "ts": 1786537552.81, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "memory.ops", "ephemeral": false, "data": {"ops": [{"op": "remember", "contact": "ct_7d1e", "facts": [{"id": "mem_03", "text": "Cita de revisión con la Dra. Vidal el 2026-08-13 a las 09:30 (BK-5521)", "source": "CA_8f4a2c"}], "took_ms": 410}]}},
|
|
124
124
|
{"seq": 124, "ts": 1786537553.46, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "call.summary", "ephemeral": false, "data": {"reason": "caller_hung_up", "outcome": "booked BK-5521: revisión Dra. Vidal, jueves 13 a las 09:30", "duration_s": 51.988, "turns": 11, "usage": [{"type": "llm_usage", "provider": "anthropic", "model": "claude-haiku-4-5", "input_tokens": 14963, "input_cached_tokens": 13145, "input_cache_creation_tokens": 562, "input_audio_tokens": 0, "input_cached_audio_tokens": 0, "input_text_tokens": 0, "input_cached_text_tokens": 0, "input_image_tokens": 0, "input_cached_image_tokens": 0, "output_tokens": 281, "output_audio_tokens": 0, "output_text_tokens": 0, "output_reasoning_tokens": 0, "session_duration": 0.0}, {"type": "tts_usage", "provider": "elevenlabs", "model": "eleven_flash_v2_5", "input_tokens": 0, "output_tokens": 0, "characters_count": 397, "audio_duration": 25.6}, {"type": "stt_usage", "provider": "soniox", "model": "stt-rt-v5", "input_tokens": 0, "output_tokens": 0, "audio_duration": 12.1}, {"type": "interruption_usage", "provider": "livekit", "model": "adaptive", "total_requests": 12}, {"type": "eot_usage", "provider": "livekit", "model": "turn-detector-v1", "total_requests": 5}], "cost": {"eur": 0.022937, "rate": {"currency": "EUR", "usd_to_eur": 0.92, "as_of": "2026-08-12"}, "rows": [{"provider": "anthropic", "model": "claude-haiku-4-5", "unit": "input_tokens", "quantity": 1256, "unit_price_usd": 1e-06, "eur": 0.001156}, {"provider": "anthropic", "model": "claude-haiku-4-5", "unit": "cached_input_tokens", "quantity": 13145, "unit_price_usd": 1e-07, "eur": 0.001209}, {"provider": "anthropic", "model": "claude-haiku-4-5", "unit": "cache_creation_tokens", "quantity": 562, "unit_price_usd": 1.25e-06, "eur": 0.000646}, {"provider": "anthropic", "model": "claude-haiku-4-5", "unit": "output_tokens", "quantity": 281, "unit_price_usd": 5e-06, "eur": 0.001293}, {"provider": "elevenlabs", "model": "eleven_flash_v2_5", "unit": "characters", "quantity": 397, "unit_price_usd": 5e-05, "eur": 0.018262}, {"provider": "soniox", "model": "stt-rt-v5", "unit": "audio_seconds", "quantity": 12.1, "unit_price_usd": 3.33e-05, "eur": 0.000371}], "unpriced": [{"provider": "livekit", "model": "adaptive"}, {"provider": "livekit", "model": "turn-detector-v1"}]}, "recording": "recordings/2026/08/12/CA_8f4a2c.ogg"}},
|
|
125
|
-
{"seq": 125, "ts": 1786537553.102, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "call.score", "ephemeral": false, "data": {"passed": false, "judges": [{"name": "consent", "verdict": "broken", "criteria": "Every irreversible tool call in this conversation ran after a confirm.granted for the same tool call and the same audience.", "reason": "book_slot ran at seq 79, before its confirm.granted at seq 93", "evidence": {"seqs": [79, 93], "said": "Sí, confirmo."}}], "panel": ["consent", "grounded"], "judge_calls": 0}},
|
|
125
|
+
{"seq": 125, "ts": 1786537553.102, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "call.score", "ephemeral": false, "data": {"passed": false, "judges": [{"name": "consent", "verdict": "broken", "criteria": "Every irreversible tool call in this conversation ran after a confirm.granted for the same tool call and the same audience.", "reason": "book_slot ran at seq 79, before its confirm.granted at seq 93", "evidence": {"seqs": [79, 93], "said": "Sí, confirmo."}}], "panel": ["consent", "grounded", "promises"], "judge_calls": 0}},
|
|
126
126
|
{"seq": 125, "ts": 1786537553.461, "call": "CA_8f4a2c", "agent": "clinica-norte", "type": "log.caught_up", "ephemeral": true, "data": {"seq": 125}}
|
|
127
127
|
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Generated from schema/rest.json: the envelopes the read doors answer in."""
|
|
2
2
|
|
|
3
|
-
from typing import Any
|
|
3
|
+
from typing import Any, Literal
|
|
4
4
|
|
|
5
5
|
from pydantic import Field
|
|
6
6
|
|
|
@@ -35,6 +35,23 @@ class LogPage(WireModel):
|
|
|
35
35
|
next: int | None
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
# One call's call.score as a list draws it: how many judges held of how many answered, and why the
|
|
39
|
+
# first one that broke did.
|
|
40
|
+
class SessionScore(WireModel):
|
|
41
|
+
"""One call's call.score as a list draws it."""
|
|
42
|
+
|
|
43
|
+
held: int
|
|
44
|
+
judged: int
|
|
45
|
+
passed: bool
|
|
46
|
+
reason: str | None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
# escalated: a person took part — a transfer, a supervisor taking the line, saying something, or
|
|
50
|
+
# ending the call. low_score: a judge answered broken. promise: the promises judge found the agent
|
|
51
|
+
# committing the business to something no tool call records.
|
|
52
|
+
type SessionFlag = Literal["escalated", "low_score", "promise"]
|
|
53
|
+
|
|
54
|
+
|
|
38
55
|
class SessionLine(WireModel):
|
|
39
56
|
"""One call as a list draws it: which call, how far the log got, and the state's own fields."""
|
|
40
57
|
|
|
@@ -53,12 +70,324 @@ class SessionLine(WireModel):
|
|
|
53
70
|
end_reason: EndReason | None
|
|
54
71
|
outcome: str | None
|
|
55
72
|
cost: Cost | None
|
|
73
|
+
score: SessionScore | None = None
|
|
74
|
+
flags: list[SessionFlag] | None = None
|
|
56
75
|
|
|
57
76
|
|
|
77
|
+
# GET /v1/agents/{slug}/sessions and GET /v1/sessions: the calls that match, newest first, a page at
|
|
78
|
+
# a time.
|
|
58
79
|
class SessionList(WireModel):
|
|
59
|
-
"""GET /v1/agents/{slug}/sessions
|
|
80
|
+
"""GET /v1/agents/{slug}/sessions and GET /v1/sessions."""
|
|
60
81
|
|
|
61
82
|
calls: list[SessionLine]
|
|
83
|
+
total: int | None = None
|
|
84
|
+
next: str | None = None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class InsightsConversations(WireModel):
|
|
88
|
+
"""How many calls started on the day, and on the day before it."""
|
|
89
|
+
|
|
90
|
+
today: int
|
|
91
|
+
yesterday: int
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class InsightsChannels(WireModel):
|
|
95
|
+
"""The day's calls by the door they came in by."""
|
|
96
|
+
|
|
97
|
+
phone: int
|
|
98
|
+
web: int
|
|
99
|
+
whatsapp: int
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class InsightsAgent(WireModel):
|
|
103
|
+
"""One agent's day."""
|
|
104
|
+
|
|
105
|
+
slug: str
|
|
106
|
+
today: int
|
|
107
|
+
score: float | None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class InsightsBudget(WireModel):
|
|
111
|
+
"""What the org may spend in a month and what it has spent so far, both worlds together."""
|
|
112
|
+
|
|
113
|
+
limit_eur: float | None
|
|
114
|
+
spent_eur_month: float
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# GET /v1/insights: one day of the key's world and corner at a glance, counted off the call index
|
|
118
|
+
# and never off a log.
|
|
119
|
+
class Insights(WireModel):
|
|
120
|
+
"""GET /v1/insights."""
|
|
121
|
+
|
|
122
|
+
day: str
|
|
123
|
+
timezone: str
|
|
124
|
+
conversations: InsightsConversations
|
|
125
|
+
resolved_rate: float | None
|
|
126
|
+
median_e2e_s: float | None
|
|
127
|
+
spend_eur: float
|
|
128
|
+
channels: InsightsChannels
|
|
129
|
+
sessions_total: int
|
|
130
|
+
live: int
|
|
131
|
+
agents: list[InsightsAgent]
|
|
132
|
+
budget: InsightsBudget
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
# GET and PUT /v1/org/judging: whether the org's calls are judged at hang-up, and what judging one
|
|
136
|
+
# may spend on a model.
|
|
137
|
+
class Judging(WireModel):
|
|
138
|
+
"""GET and PUT /v1/org/judging."""
|
|
139
|
+
|
|
140
|
+
on: bool
|
|
141
|
+
ceiling_eur: float | None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class JudgingWanted(WireModel):
|
|
145
|
+
"""PUT /v1/org/judging, the body."""
|
|
146
|
+
|
|
147
|
+
on: bool
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# in: the contact wrote it. out: the agent, or a person as the agent, did. call: a spoken call,
|
|
151
|
+
# drawn as one pill.
|
|
152
|
+
type ThreadKind = Literal["in", "out", "call"]
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class ThreadLast(WireModel):
|
|
156
|
+
"""The newest thing on a contact's thread."""
|
|
157
|
+
|
|
158
|
+
text: str | None
|
|
159
|
+
at: float
|
|
160
|
+
kind: ThreadKind
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class ThreadLine(WireModel):
|
|
164
|
+
"""One contact of an agent's inbox: every call of theirs, folded into one line."""
|
|
165
|
+
|
|
166
|
+
contact: str
|
|
167
|
+
name: str | None
|
|
168
|
+
channel_last: Channel
|
|
169
|
+
last: ThreadLast
|
|
170
|
+
unread: int
|
|
171
|
+
calls: int
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class ThreadList(WireModel):
|
|
175
|
+
"""GET /v1/agents/{slug}/threads: the agent's contacts, the newest thread first."""
|
|
176
|
+
|
|
177
|
+
threads: list[ThreadLine]
|
|
178
|
+
next: str | None
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class ThreadMessage(WireModel):
|
|
182
|
+
"""One message of a thread, or one spoken call drawn as a pill."""
|
|
183
|
+
|
|
184
|
+
kind: ThreadKind
|
|
185
|
+
text: str | None
|
|
186
|
+
at: float
|
|
187
|
+
call: str
|
|
188
|
+
channel: Channel
|
|
189
|
+
duration_s: float | None = None
|
|
190
|
+
answered: bool | None = None
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class Thread(WireModel):
|
|
194
|
+
"""GET /v1/agents/{slug}/threads/{contact}: every call of one contact, merged, oldest first."""
|
|
195
|
+
|
|
196
|
+
contact: str
|
|
197
|
+
name: str | None
|
|
198
|
+
messages: list[ThreadMessage]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class ThreadSay(WireModel):
|
|
202
|
+
"""POST /v1/agents/{slug}/threads/{contact}/messages, the body."""
|
|
203
|
+
|
|
204
|
+
text: str
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# The turn.agent it lands as is on that call's log.
|
|
208
|
+
class ThreadSaid(WireModel):
|
|
209
|
+
"""POST /v1/agents/{slug}/threads/{contact}/messages, the answer: the call it was said on."""
|
|
210
|
+
|
|
211
|
+
contact: str
|
|
212
|
+
call: str
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class AgentFact(WireModel):
|
|
216
|
+
"""One current fact memory holds, across the contacts an agent's calls taught."""
|
|
217
|
+
|
|
218
|
+
id: str
|
|
219
|
+
contact: str
|
|
220
|
+
text: str
|
|
221
|
+
category: str | None
|
|
222
|
+
written_at: float
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class AgentMemory(WireModel):
|
|
226
|
+
"""GET /v1/agents/{slug}/memory: the current facts the agent's calls taught, newest first."""
|
|
227
|
+
|
|
228
|
+
facts: list[AgentFact]
|
|
229
|
+
next: str | None
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# GET and PUT /v1/agents/{slug}/widget: how the widget presents this agent, kept per org, world and
|
|
233
|
+
# agent. PUT takes the whole set.
|
|
234
|
+
class WidgetSettings(WireModel):
|
|
235
|
+
"""GET and PUT /v1/agents/{slug}/widget."""
|
|
236
|
+
|
|
237
|
+
title: str | None
|
|
238
|
+
tagline: str | None
|
|
239
|
+
greeting: str | None
|
|
240
|
+
accent: str | None
|
|
241
|
+
autostart: bool
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
# GET and PUT /v1/org/sso: the OpenID Connect provider this org's people sign in at, and never the
|
|
245
|
+
# client secret it was wired with.
|
|
246
|
+
class OrgSso(WireModel):
|
|
247
|
+
"""GET and PUT /v1/org/sso."""
|
|
248
|
+
|
|
249
|
+
configured: bool
|
|
250
|
+
issuer: str | None
|
|
251
|
+
client_id: str | None
|
|
252
|
+
domains: list[str]
|
|
253
|
+
role: str | None
|
|
254
|
+
required: bool
|
|
255
|
+
redirect_uri: str
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
# The configuration is replaced whole, the client secret included: it is write-only, so a change of
|
|
259
|
+
# anything else carries it again.
|
|
260
|
+
class OrgSsoWanted(WireModel):
|
|
261
|
+
"""PUT /v1/org/sso, the body."""
|
|
262
|
+
|
|
263
|
+
issuer: str
|
|
264
|
+
client_id: str
|
|
265
|
+
client_secret: str
|
|
266
|
+
domains: list[str]
|
|
267
|
+
role: str | None = None
|
|
268
|
+
required: bool | None = None
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# GET and PUT /v1/org/mail: the SMTP account this org's letters go out through, how the last one
|
|
272
|
+
# went, and never the password it was wired with. An org that wired none sends through the box's own
|
|
273
|
+
# mail, or through nothing.
|
|
274
|
+
class OrgMail(WireModel):
|
|
275
|
+
"""GET and PUT /v1/org/mail."""
|
|
276
|
+
|
|
277
|
+
configured: bool
|
|
278
|
+
host: str | None
|
|
279
|
+
port: int | None
|
|
280
|
+
security: str | None
|
|
281
|
+
username: str | None
|
|
282
|
+
from_: str | None = Field(alias="from")
|
|
283
|
+
verified_at: str | None
|
|
284
|
+
last_error: str | None
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# The account is replaced whole, the password included: it is write-only, so a change of anything
|
|
288
|
+
# else carries it again — and the standing is reset with it, since what a server said about the old
|
|
289
|
+
# password is not news about a new one.
|
|
290
|
+
class OrgMailWanted(WireModel):
|
|
291
|
+
"""PUT /v1/org/mail, the body."""
|
|
292
|
+
|
|
293
|
+
host: str
|
|
294
|
+
port: int
|
|
295
|
+
security: Literal["starttls", "tls", "none"] | None = None
|
|
296
|
+
username: str | None = None
|
|
297
|
+
password: str | None = None
|
|
298
|
+
from_: str = Field(alias="from")
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# GET and PUT /v1/ops/mail: the SMTP account the BOX posts its letters through — what the operator
|
|
302
|
+
# stored, or the environment's two variables — how the last one went, and never the password. An
|
|
303
|
+
# org's own account (OrgMail) still wins over both.
|
|
304
|
+
class BoxMail(WireModel):
|
|
305
|
+
"""GET and PUT /v1/ops/mail."""
|
|
306
|
+
|
|
307
|
+
configured: bool
|
|
308
|
+
source: str | None
|
|
309
|
+
host: str | None
|
|
310
|
+
port: int | None
|
|
311
|
+
security: str | None
|
|
312
|
+
username: str | None
|
|
313
|
+
from_: str | None = Field(alias="from")
|
|
314
|
+
verified_at: str | None
|
|
315
|
+
last_error: str | None
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
# GET and PUT /v1/ops/brand, and `brand` on GET /.well-known/pinecall: what this box's letters and
|
|
319
|
+
# its sign-in page are called and painted with. Pinecall, its accent and no logo until the operator
|
|
320
|
+
# says.
|
|
321
|
+
class BoxBrand(WireModel):
|
|
322
|
+
"""GET and PUT /v1/ops/brand, and `brand` on GET /.well-known/pinecall."""
|
|
323
|
+
|
|
324
|
+
name: str
|
|
325
|
+
logo_url: str | None
|
|
326
|
+
accent: str
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
# A field left out keeps what it had; an empty string goes back to the default — which for the logo
|
|
330
|
+
# is none at all, the only way to clear one.
|
|
331
|
+
class BoxBrandWanted(WireModel):
|
|
332
|
+
"""PUT /v1/ops/brand, the body."""
|
|
333
|
+
|
|
334
|
+
name: str | None = None
|
|
335
|
+
logo_url: str | None = None
|
|
336
|
+
accent: str | None = None
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
# One box-wide identity provider as the operator reads it: whether it is usable, the client this
|
|
340
|
+
# gateway is at it, and the URI to register there. Never the secret.
|
|
341
|
+
class BoxProvider(WireModel):
|
|
342
|
+
"""One box-wide identity provider as the operator reads it."""
|
|
343
|
+
|
|
344
|
+
configured: bool
|
|
345
|
+
client_id: str | None
|
|
346
|
+
redirect_uri: str
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
# GET /v1/ops/signin: every provider this box can offer every org's people, wired or not, one key
|
|
350
|
+
# each. Only `google` today; a second is a key here and a row on the box.
|
|
351
|
+
class BoxSignIn(WireModel):
|
|
352
|
+
"""GET /v1/ops/signin."""
|
|
353
|
+
|
|
354
|
+
google: BoxProvider
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
# PUT /v1/ops/signin/google, the body: the OAuth client the operator made at Google for this
|
|
358
|
+
# gateway. Replaced whole, the secret write-only; the issuer is Google's own and is not a field.
|
|
359
|
+
class BoxProviderWanted(WireModel):
|
|
360
|
+
"""PUT /v1/ops/signin/google, the body."""
|
|
361
|
+
|
|
362
|
+
client_id: str
|
|
363
|
+
client_secret: str
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
# POST /v1/org/mail/test: one letter, waited for — the only door of this runtime that waits for a
|
|
367
|
+
# mail server, because it is the one a person is watching.
|
|
368
|
+
class MailSent(WireModel):
|
|
369
|
+
"""POST /v1/org/mail/test."""
|
|
370
|
+
|
|
371
|
+
sent: bool
|
|
372
|
+
error: str | None
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# One org a sign-in page may send somebody to, named — and nothing about whether anybody answers to
|
|
376
|
+
# the address that asked.
|
|
377
|
+
class SsoOrg(WireModel):
|
|
378
|
+
"""SsoOrg, as protocol/schema declares it."""
|
|
379
|
+
|
|
380
|
+
org: str
|
|
381
|
+
slug: str
|
|
382
|
+
name: str
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
# POST /v1/login/sso/discover: the orgs whose domains match an address's and that wired a provider.
|
|
386
|
+
# Empty is the answer for a domain nobody wired, and for a box that keeps no provider at all.
|
|
387
|
+
class SsoDiscovery(WireModel):
|
|
388
|
+
"""POST /v1/login/sso/discover."""
|
|
389
|
+
|
|
390
|
+
orgs: list[SsoOrg]
|
|
62
391
|
|
|
63
392
|
|
|
64
393
|
# One corner of a world holding an agent: the member whose it is, named so a person can read it.
|
|
@@ -342,3 +671,45 @@ class Remembered(WireModel):
|
|
|
342
671
|
|
|
343
672
|
ops: int
|
|
344
673
|
took_ms: float
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
# POST /v1/agents/{slug}/dial, the answer: the call a dial became, accepted before the far end has
|
|
677
|
+
# heard anything ring.
|
|
678
|
+
class Dialled(WireModel):
|
|
679
|
+
"""POST /v1/agents/{slug}/dial, the answer."""
|
|
680
|
+
|
|
681
|
+
call: str
|
|
682
|
+
agent: str
|
|
683
|
+
to: str
|
|
684
|
+
from_: str = Field(alias="from")
|
|
685
|
+
env: Env
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
class DialGuards(WireModel):
|
|
689
|
+
"""GET /v1/carrier/outbound, the guards: what this org may dial and how often."""
|
|
690
|
+
|
|
691
|
+
dial_anywhere: bool
|
|
692
|
+
per_minute: int
|
|
693
|
+
per_day: int
|
|
694
|
+
max_duration_s: int
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
# GET /v1/carrier/outbound, the answer: whether this org can place a call yet, and what is missing.
|
|
698
|
+
class CarrierOutbound(WireModel):
|
|
699
|
+
"""GET /v1/carrier/outbound, the answer."""
|
|
700
|
+
|
|
701
|
+
ready: bool
|
|
702
|
+
kind: str | None
|
|
703
|
+
from_numbers: list[str]
|
|
704
|
+
steps_missing: list[str]
|
|
705
|
+
guards: DialGuards
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
class OutboundProvisioned(WireModel):
|
|
709
|
+
"""POST /v1/carrier/outbound, the answer: the plan, and whether it was carried out."""
|
|
710
|
+
|
|
711
|
+
steps: list[str]
|
|
712
|
+
dry_run: bool
|
|
713
|
+
ready: bool
|
|
714
|
+
trunk: str | None = None
|
|
715
|
+
address: str | None = None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|