pinecall-runtime 0.1.6__py3-none-any.whl

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.
Files changed (328) hide show
  1. pinecall/__init__.py +0 -0
  2. pinecall/channels/__init__.py +0 -0
  3. pinecall/channels/_chooser.py +45 -0
  4. pinecall/channels/offers.py +219 -0
  5. pinecall/channels/rooms.py +176 -0
  6. pinecall/channels/routes.py +275 -0
  7. pinecall/channels/telephony/__init__.py +0 -0
  8. pinecall/channels/telephony/_twilio.py +349 -0
  9. pinecall/channels/telephony/carrier.py +229 -0
  10. pinecall/channels/telephony/carrier_catalog.py +92 -0
  11. pinecall/channels/telephony/carriers.csv +41 -0
  12. pinecall/channels/telephony/dialing.py +419 -0
  13. pinecall/channels/telephony/firewall.py +58 -0
  14. pinecall/channels/telephony/hand_over.py +78 -0
  15. pinecall/channels/telephony/number_path.py +176 -0
  16. pinecall/channels/telephony/numbers.py +530 -0
  17. pinecall/channels/telephony/origination.py +77 -0
  18. pinecall/channels/telephony/sip.py +448 -0
  19. pinecall/channels/whatsapp.py +413 -0
  20. pinecall/cli/__init__.py +0 -0
  21. pinecall/cli/_archive.py +85 -0
  22. pinecall/cli/_drift.py +38 -0
  23. pinecall/cli/_facts.py +67 -0
  24. pinecall/cli/_load.py +433 -0
  25. pinecall/cli/_operator.py +626 -0
  26. pinecall/cli/_sessions.py +164 -0
  27. pinecall/cli/_traceback.py +95 -0
  28. pinecall/cli/_usage.py +34 -0
  29. pinecall/cli/main.py +521 -0
  30. pinecall/domain/__init__.py +0 -0
  31. pinecall/domain/agent.py +446 -0
  32. pinecall/domain/call.py +152 -0
  33. pinecall/domain/country_codes.py +32 -0
  34. pinecall/domain/errors.py +151 -0
  35. pinecall/domain/names.py +115 -0
  36. pinecall/domain/org.py +120 -0
  37. pinecall/domain/person.py +208 -0
  38. pinecall/domain/scope.py +21 -0
  39. pinecall/evals/__init__.py +0 -0
  40. pinecall/evals/_evidence.py +158 -0
  41. pinecall/evals/calibration.py +112 -0
  42. pinecall/evals/callers.py +160 -0
  43. pinecall/evals/case.py +327 -0
  44. pinecall/evals/checks.py +437 -0
  45. pinecall/evals/compliance.py +197 -0
  46. pinecall/evals/dataset.py +451 -0
  47. pinecall/evals/goldens.py +331 -0
  48. pinecall/evals/judges.py +525 -0
  49. pinecall/evals/runs.py +252 -0
  50. pinecall/evals/spoken.py +356 -0
  51. pinecall/fleet/__init__.py +0 -0
  52. pinecall/fleet/client.py +569 -0
  53. pinecall/fleet/demand.py +51 -0
  54. pinecall/fleet/heartbeat.py +194 -0
  55. pinecall/fleet/measures.py +157 -0
  56. pinecall/fleet/roster.py +232 -0
  57. pinecall/fleet/worlds.py +44 -0
  58. pinecall/gateway/__init__.py +0 -0
  59. pinecall/gateway/_call_setup.py +53 -0
  60. pinecall/gateway/_deps.py +556 -0
  61. pinecall/gateway/_gateway.py +75 -0
  62. pinecall/gateway/_served.py +396 -0
  63. pinecall/gateway/_sockets.py +444 -0
  64. pinecall/gateway/_streams.py +83 -0
  65. pinecall/gateway/_text_calls.py +182 -0
  66. pinecall/gateway/api/__init__.py +0 -0
  67. pinecall/gateway/api/accounts.py +324 -0
  68. pinecall/gateway/api/agents.py +150 -0
  69. pinecall/gateway/api/apps.py +478 -0
  70. pinecall/gateway/api/box.py +658 -0
  71. pinecall/gateway/api/calibration.py +58 -0
  72. pinecall/gateway/api/callbacks.py +105 -0
  73. pinecall/gateway/api/calls.py +655 -0
  74. pinecall/gateway/api/chat.py +223 -0
  75. pinecall/gateway/api/dataset.py +163 -0
  76. pinecall/gateway/api/desk.py +113 -0
  77. pinecall/gateway/api/entries.py +77 -0
  78. pinecall/gateway/api/evals.py +647 -0
  79. pinecall/gateway/api/fleet.py +73 -0
  80. pinecall/gateway/api/hosting.py +241 -0
  81. pinecall/gateway/api/judges.py +97 -0
  82. pinecall/gateway/api/keys.py +110 -0
  83. pinecall/gateway/api/line.py +142 -0
  84. pinecall/gateway/api/members.py +197 -0
  85. pinecall/gateway/api/metrics.py +226 -0
  86. pinecall/gateway/api/numbers.py +476 -0
  87. pinecall/gateway/api/ops.py +428 -0
  88. pinecall/gateway/api/org.py +283 -0
  89. pinecall/gateway/api/personas.py +157 -0
  90. pinecall/gateway/api/pipeline.py +206 -0
  91. pinecall/gateway/api/prompts.py +50 -0
  92. pinecall/gateway/api/providers.py +240 -0
  93. pinecall/gateway/api/recordings.py +89 -0
  94. pinecall/gateway/api/relay.py +169 -0
  95. pinecall/gateway/api/retrieval.py +403 -0
  96. pinecall/gateway/api/runner.py +109 -0
  97. pinecall/gateway/api/sessions.py +102 -0
  98. pinecall/gateway/api/settings.py +463 -0
  99. pinecall/gateway/api/signup.py +164 -0
  100. pinecall/gateway/api/sso_login.py +168 -0
  101. pinecall/gateway/api/threads.py +187 -0
  102. pinecall/gateway/api/usage.py +303 -0
  103. pinecall/gateway/api/visitors.py +222 -0
  104. pinecall/gateway/api/whatsapp.py +48 -0
  105. pinecall/gateway/api/widget.py +49 -0
  106. pinecall/gateway/app.py +504 -0
  107. pinecall/gateway/calls/__init__.py +0 -0
  108. pinecall/gateway/calls/binding.py +63 -0
  109. pinecall/gateway/calls/commands.py +18 -0
  110. pinecall/gateway/calls/inbox.py +100 -0
  111. pinecall/gateway/calls/known.py +23 -0
  112. pinecall/gateway/calls/owners.py +87 -0
  113. pinecall/gateway/calls/pump.py +33 -0
  114. pinecall/gateway/calls/serving.py +118 -0
  115. pinecall/gateway/calls/threads.py +367 -0
  116. pinecall/gateway/dispatching/__init__.py +0 -0
  117. pinecall/gateway/dispatching/arrivals.py +58 -0
  118. pinecall/gateway/dispatching/sweep.py +43 -0
  119. pinecall/gateway/ending/__init__.py +0 -0
  120. pinecall/gateway/ending/reaper.py +160 -0
  121. pinecall/gateway/ending/seal.py +312 -0
  122. pinecall/gateway/ending/stranded.py +117 -0
  123. pinecall/log/__init__.py +0 -0
  124. pinecall/log/_histogram.py +74 -0
  125. pinecall/log/_relay.py +259 -0
  126. pinecall/log/_writer.py +454 -0
  127. pinecall/log/days.py +154 -0
  128. pinecall/log/drift.py +550 -0
  129. pinecall/log/facts.py +358 -0
  130. pinecall/log/inbox.py +169 -0
  131. pinecall/log/lists.py +155 -0
  132. pinecall/log/logs.py +581 -0
  133. pinecall/log/openings.py +71 -0
  134. pinecall/log/private.py +135 -0
  135. pinecall/log/queries.py +229 -0
  136. pinecall/log/readers.py +207 -0
  137. pinecall/log/reduce.py +659 -0
  138. pinecall/log/refold.py +217 -0
  139. pinecall/log/store.py +370 -0
  140. pinecall/log/usage.py +242 -0
  141. pinecall/postgres/__init__.py +0 -0
  142. pinecall/postgres/migrate.py +202 -0
  143. pinecall/postgres/migrations/0001_schema.sql +591 -0
  144. pinecall/postgres/migrations/0002_worlds.sql +15 -0
  145. pinecall/postgres/migrations/0003_lent.sql +4 -0
  146. pinecall/postgres/migrations/0004_carriers.sql +23 -0
  147. pinecall/postgres/migrations/0005_retrieved_only.sql +11 -0
  148. pinecall/postgres/migrations/0006_persona_agents.sql +9 -0
  149. pinecall/postgres/migrations/0007_money_in_dollars.sql +27 -0
  150. pinecall/postgres/migrations/0008_lexicon_per_agent.sql +27 -0
  151. pinecall/postgres/migrations/0009_personas_per_agent.sql +31 -0
  152. pinecall/postgres/migrations/0010_agent_judges.sql +12 -0
  153. pinecall/postgres/migrations/0011_erasure.sql +32 -0
  154. pinecall/postgres/migrations/0012_org_policy.sql +10 -0
  155. pinecall/postgres/migrations/0013_calling_rules.sql +12 -0
  156. pinecall/postgres/migrations/0014_consents.sql +24 -0
  157. pinecall/postgres/migrations/0015_disclosure.sql +7 -0
  158. pinecall/postgres/migrations/0016_call_records.sql +21 -0
  159. pinecall/postgres/migrations/0017_reads.sql +17 -0
  160. pinecall/postgres/migrations/0018_hosting.sql +41 -0
  161. pinecall/postgres/migrations/0019_hosted_state.sql +10 -0
  162. pinecall/postgres/migrations/0020_written_once.sql +7 -0
  163. pinecall/postgres/migrations/0021_hosted_running.sql +21 -0
  164. pinecall/postgres/migrations/0022_sealed.sql +39 -0
  165. pinecall/postgres/migrations/0023_usage_totals.sql +158 -0
  166. pinecall/postgres/migrations/0024_log_key_by_day.sql +8 -0
  167. pinecall/postgres/migrations/0025_log_before_the_days.sql +15 -0
  168. pinecall/postgres/migrations/0026_log_before_the_days_validated.sql +4 -0
  169. pinecall/postgres/migrations/0027_log_by_day.sql +96 -0
  170. pinecall/postgres/migrations/0030_whatsapp_seen.sql +17 -0
  171. pinecall/postgres/migrations/0040_reads_widened.sql +15 -0
  172. pinecall/postgres/migrations/0060_drift.sql +62 -0
  173. pinecall/postgres/migrations/0061_canaries.sql +24 -0
  174. pinecall/postgres/migrations/0062_eval_cases.sql +23 -0
  175. pinecall/postgres/migrations/0063_prompts.sql +16 -0
  176. pinecall/postgres/migrations/0075_call_private.sql +14 -0
  177. pinecall/postgres/migrations/0076_recording_keys.sql +13 -0
  178. pinecall/postgres/migrations/0077_tokens_world.sql +7 -0
  179. pinecall/postgres/migrations/0078_facts_cost_by_stage.sql +12 -0
  180. pinecall/postgres/migrations/0079_judge_labels.sql +20 -0
  181. pinecall/postgres/migrations/0080_call_openings.sql +20 -0
  182. pinecall/postgres/migrations/0081_seal_lease.sql +7 -0
  183. pinecall/postgres/migrations/0082_caller_codes.sql +14 -0
  184. pinecall/postgres/migrations/0083_one_use_words.sql +15 -0
  185. pinecall/postgres/migrations/0084_knocks_and_run_leases.sql +23 -0
  186. pinecall/postgres/migrations/0085_seal_lease_holder.sql +8 -0
  187. pinecall/postgres/migrations/0086_route_origin.sql +17 -0
  188. pinecall/postgres/migrations/0087_carrier_networks.sql +20 -0
  189. pinecall/postgres/migrations/0088_route_last_call.sql +3 -0
  190. pinecall/postgres/migrations/0089_offers.sql +21 -0
  191. pinecall/postgres/migrations/0090_invitation_purpose.sql +5 -0
  192. pinecall/postgres/migrations/0091_head_opened_by.sql +6 -0
  193. pinecall/postgres/migrations/0092_key_parent.sql +6 -0
  194. pinecall/postgres/migrations/0093_code_tries.sql +10 -0
  195. pinecall/postgres/migrations/0094_route_approval.sql +12 -0
  196. pinecall/postgres/migrations/0095_sso_domain_proofs.sql +17 -0
  197. pinecall/postgres/migrations/0096_row_level_security.sql +38 -0
  198. pinecall/postgres/migrations/0097_case_status.sql +19 -0
  199. pinecall/postgres/pool.py +190 -0
  200. pinecall/process/__init__.py +0 -0
  201. pinecall/process/_objects.py +97 -0
  202. pinecall/process/box_settings.py +23 -0
  203. pinecall/process/connections.py +124 -0
  204. pinecall/process/metrics.py +127 -0
  205. pinecall/process/recordings.py +323 -0
  206. pinecall/process/resolver.py +41 -0
  207. pinecall/process/sealed_audio.py +173 -0
  208. pinecall/process/settings.py +534 -0
  209. pinecall/process/shared.py +158 -0
  210. pinecall/process/signal.py +416 -0
  211. pinecall/providers/__init__.py +0 -0
  212. pinecall/providers/build.py +570 -0
  213. pinecall/providers/catalog.py +266 -0
  214. pinecall/providers/credentials.py +230 -0
  215. pinecall/providers/declared.py +69 -0
  216. pinecall/providers/prices.py +240 -0
  217. pinecall/providers/tried.py +104 -0
  218. pinecall/providers/voices.py +174 -0
  219. pinecall/public/console/assets/index-BIfKPSgn.js +164 -0
  220. pinecall/public/console/assets/index-ClOTU-xX.css +1 -0
  221. pinecall/public/console/favicon.png +0 -0
  222. pinecall/public/console/index.html +20 -0
  223. pinecall/public/console/logo-mark.png +0 -0
  224. pinecall/public/console/pinecall-logo-dark.png +0 -0
  225. pinecall/public/console/pinecall-logo.png +0 -0
  226. pinecall/public/console/pinecall-mark.png +0 -0
  227. pinecall/public/console/sw.js +37 -0
  228. pinecall/public/widget/pinecall-widget.js +853 -0
  229. pinecall/py.typed +0 -0
  230. pinecall/retrieval/__init__.py +0 -0
  231. pinecall/retrieval/_search.py +186 -0
  232. pinecall/retrieval/_sections.py +70 -0
  233. pinecall/retrieval/embed.py +340 -0
  234. pinecall/retrieval/extraction.py +403 -0
  235. pinecall/retrieval/knowledge.py +659 -0
  236. pinecall/retrieval/lookups.py +192 -0
  237. pinecall/retrieval/memory.py +603 -0
  238. pinecall/runner/__init__.py +0 -0
  239. pinecall/runner/_kube.py +459 -0
  240. pinecall/runner/_plan.py +157 -0
  241. pinecall/runner/main.py +306 -0
  242. pinecall/session/__init__.py +0 -0
  243. pinecall/session/_agent.py +141 -0
  244. pinecall/session/_hearing.py +101 -0
  245. pinecall/session/_livekit.py +195 -0
  246. pinecall/session/_prompt.py +147 -0
  247. pinecall/session/_recording.py +237 -0
  248. pinecall/session/call.py +367 -0
  249. pinecall/session/clock.py +40 -0
  250. pinecall/session/hold.py +239 -0
  251. pinecall/session/room.py +626 -0
  252. pinecall/session/session.py +690 -0
  253. pinecall/session/text.py +144 -0
  254. pinecall/session/tools.py +483 -0
  255. pinecall/session/voice.py +118 -0
  256. pinecall/session/widget.py +201 -0
  257. pinecall/tenancy/__init__.py +0 -0
  258. pinecall/tenancy/admission.py +204 -0
  259. pinecall/tenancy/agents.py +164 -0
  260. pinecall/tenancy/canary.py +126 -0
  261. pinecall/tenancy/carrier_networks.py +156 -0
  262. pinecall/tenancy/carriers.py +265 -0
  263. pinecall/tenancy/codes.py +196 -0
  264. pinecall/tenancy/consents.py +167 -0
  265. pinecall/tenancy/dial_policy.py +405 -0
  266. pinecall/tenancy/disclosure.py +41 -0
  267. pinecall/tenancy/erasure.py +204 -0
  268. pinecall/tenancy/export.py +167 -0
  269. pinecall/tenancy/hosted_running.py +166 -0
  270. pinecall/tenancy/hosting.py +475 -0
  271. pinecall/tenancy/judges.py +126 -0
  272. pinecall/tenancy/keys.py +467 -0
  273. pinecall/tenancy/knocks.py +40 -0
  274. pinecall/tenancy/letters.py +378 -0
  275. pinecall/tenancy/mail.py +458 -0
  276. pinecall/tenancy/org_secrets.py +123 -0
  277. pinecall/tenancy/orgs.py +78 -0
  278. pinecall/tenancy/people.py +543 -0
  279. pinecall/tenancy/personas.py +145 -0
  280. pinecall/tenancy/policy.py +65 -0
  281. pinecall/tenancy/prompts.py +48 -0
  282. pinecall/tenancy/reads.py +73 -0
  283. pinecall/tenancy/recording_keys.py +41 -0
  284. pinecall/tenancy/remembered.py +148 -0
  285. pinecall/tenancy/retention.py +86 -0
  286. pinecall/tenancy/scopes.py +406 -0
  287. pinecall/tenancy/signin.py +622 -0
  288. pinecall/tenancy/spend.py +69 -0
  289. pinecall/tenancy/sso.py +431 -0
  290. pinecall/tenancy/sso_domains.py +132 -0
  291. pinecall/tenancy/throttle.py +88 -0
  292. pinecall/tenancy/tokens.py +400 -0
  293. pinecall/tenancy/traceback.py +70 -0
  294. pinecall/tenancy/usage.py +136 -0
  295. pinecall/tenancy/vault.py +204 -0
  296. pinecall/tenancy/words.py +144 -0
  297. pinecall/wire/__init__.py +0 -0
  298. pinecall/wire/commands.py +350 -0
  299. pinecall/wire/events.py +655 -0
  300. pinecall/wire/frames.py +65 -0
  301. pinecall/wire/metrics.py +287 -0
  302. pinecall/wire/parts.py +320 -0
  303. pinecall/wire/rest/__init__.py +0 -0
  304. pinecall/wire/rest/accounts.py +512 -0
  305. pinecall/wire/rest/agents.py +172 -0
  306. pinecall/wire/rest/calls.py +397 -0
  307. pinecall/wire/rest/evals.py +453 -0
  308. pinecall/wire/rest/fleet.py +69 -0
  309. pinecall/wire/rest/hosting.py +174 -0
  310. pinecall/wire/rest/numbers.py +280 -0
  311. pinecall/wire/rest/ops.py +342 -0
  312. pinecall/wire/rest/providers.py +83 -0
  313. pinecall/wire/rest/retrieval.py +301 -0
  314. pinecall/wire/rest/settings.py +184 -0
  315. pinecall/wire/rest/usage.py +245 -0
  316. pinecall/wire/room.py +61 -0
  317. pinecall/wire/scores.py +49 -0
  318. pinecall/wire/state.py +243 -0
  319. pinecall/worker/__init__.py +0 -0
  320. pinecall/worker/_job.py +655 -0
  321. pinecall/worker/_recorder.py +82 -0
  322. pinecall/worker/_traces.py +48 -0
  323. pinecall/worker/main.py +307 -0
  324. pinecall_runtime-0.1.6.dist-info/METADATA +212 -0
  325. pinecall_runtime-0.1.6.dist-info/RECORD +328 -0
  326. pinecall_runtime-0.1.6.dist-info/WHEEL +4 -0
  327. pinecall_runtime-0.1.6.dist-info/entry_points.txt +2 -0
  328. pinecall_runtime-0.1.6.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,280 @@
1
+ """The number doors' bodies: carriers, numbers, dialling out, a leg's trunk, consent, opt-outs."""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import Field
6
+
7
+ from pinecall.domain.call import Route
8
+ from pinecall.domain.names import Channel, Env, RouteOrigin, StepState
9
+ from pinecall.wire.frames import WireModel
10
+ from pinecall.wire.parts import (
11
+ Projection,
12
+ )
13
+
14
+ # ── dialling out ──
15
+
16
+ type ConsentKind = Literal["express", "written", "opt_out"]
17
+
18
+
19
+ class DialResponse(WireModel):
20
+ """POST /v1/agents/{slug}/dial, the answer."""
21
+
22
+ call: str
23
+ agent: str
24
+ to: str
25
+ from_: str = Field(alias="from")
26
+ env: Env
27
+ log_token: str
28
+
29
+
30
+ class DialGuards(WireModel):
31
+ """GET /v1/carrier/outbound, the guards: what this org may dial and how often."""
32
+
33
+ dial_anywhere: bool
34
+ per_minute: int
35
+ per_day: int
36
+ max_duration_s: int
37
+
38
+
39
+ class OutboundStatus(WireModel):
40
+ """GET /v1/carrier/outbound, the answer."""
41
+
42
+ ready: bool
43
+ kind: str | None
44
+ from_numbers: list[str]
45
+ steps_missing: list[str]
46
+ guards: DialGuards
47
+
48
+
49
+ class ProvisionOutboundResponse(WireModel):
50
+ """POST /v1/carrier/outbound, the answer: the plan, and whether it was carried out."""
51
+
52
+ steps: list[str]
53
+ dry_run: bool
54
+ ready: bool
55
+ trunk: str | None = None
56
+ address: str | None = None
57
+
58
+
59
+ # ── numbers and the accounts they live in ──
60
+
61
+
62
+ class NetworkRow(WireModel):
63
+ """A network a peer of the org calls from, and whether the box's operator admitted it."""
64
+
65
+ network: str
66
+ state: str
67
+
68
+
69
+ class CarrierRow(WireModel):
70
+ """GET /v1/carrier: one account of the org, named, never its secret."""
71
+
72
+ kind: str
73
+ account: str
74
+ label: str = ""
75
+ # A SIP peer's networks, each waiting, approved or refused; none for an account with an API.
76
+ networks: list[NetworkRow] = Field(default_factory=list[NetworkRow])
77
+
78
+
79
+ class CatalogCarrier(WireModel):
80
+ """A carrier the org may bring numbers through: automatic (its API) or guided (SIP terms)."""
81
+
82
+ kind: str
83
+ name: str
84
+ how: Literal["automatic", "guided"]
85
+ networks: list[str]
86
+
87
+
88
+ class CarrierCatalog(WireModel):
89
+ """GET /v1/carriers/catalog: the carriers this box admits, and whether it sells numbers."""
90
+
91
+ carriers: list[CatalogCarrier]
92
+ sells: bool
93
+
94
+
95
+ class CarrierList(WireModel):
96
+ """GET /v1/carriers: every account of the org, oldest first."""
97
+
98
+ carriers: list[CarrierRow]
99
+
100
+
101
+ class OwnedNumberRow(WireModel):
102
+ """A number an account owns, and whether this world imported it."""
103
+
104
+ number: str
105
+ name: str
106
+ imported: bool
107
+ account: str
108
+
109
+
110
+ class AvailableNumbers(WireModel):
111
+ """GET /v1/numbers/available: what the org's Twilio accounts own; a peer lists none."""
112
+
113
+ kind: str
114
+ numbers: list[OwnedNumberRow]
115
+
116
+
117
+ class NumberRow(WireModel):
118
+ """GET /v1/numbers, one row: a number of the org, the agent it reaches, how it was written."""
119
+
120
+ route: Route
121
+ origin: RouteOrigin
122
+ # What a call to it does now, read off the tables (GET /v1/numbers/{number}/path says why).
123
+ rings: StepState = "ok"
124
+ # When a call to it last reached the box; null when none ever did.
125
+ last_call_at: float | None = None
126
+ # The carrier of the box's catalog a hooked number comes through.
127
+ via: str | None = None
128
+ # The org's account the number lives in (GET /v1/carriers names it); null for one with none.
129
+ account: str | None = None
130
+
131
+
132
+ class PathStep(WireModel):
133
+ """One step of a call's way to its agent, what it does now, and what would make it work."""
134
+
135
+ step: Literal["carrier", "fence", "world", "agent"]
136
+ state: StepState
137
+ says: str
138
+ fix: str | None = None
139
+
140
+
141
+ class NumberPath(WireModel):
142
+ """GET /v1/numbers/{number}/path: the four steps, the worst of them, and the last call."""
143
+
144
+ number: str
145
+ steps: list[PathStep]
146
+ rings: StepState
147
+ last_call_at: float | None
148
+
149
+
150
+ class ImportNumberResponse(WireModel):
151
+ """POST /v1/numbers and /v1/numbers/buy: the route, one sentence a step, whether it ran."""
152
+
153
+ route: Route
154
+ steps: list[str]
155
+ dry_run: bool
156
+
157
+
158
+ class ImportNumberRequest(WireModel):
159
+ """POST /v1/numbers: a number of one of the org's accounts, or one the org hooks itself."""
160
+
161
+ number: str
162
+ agent: str
163
+ channel: Channel = "phone"
164
+ account: str | None = None
165
+ hooked: bool = False
166
+ networks: list[str] = Field(default_factory=list[str])
167
+ # A hooked number's carrier from the box's catalog (GET /v1/carriers/catalog).
168
+ via: str | None = None
169
+ move: bool = False
170
+
171
+
172
+ class BuyNumberRequest(WireModel):
173
+ """POST /v1/numbers/buy: a number wanted from the box's own account."""
174
+
175
+ # Two letters and digits alone: both ride a URL path the box signs with its own account.
176
+ country: str = Field(pattern=r"^[A-Za-z]{2}$")
177
+ area_code: str | None = Field(default=None, pattern=r"^[0-9]{1,6}$")
178
+ agent: str
179
+ channel: Channel = "phone"
180
+
181
+
182
+ class MoveNumberRequest(WireModel):
183
+ """PUT /v1/numbers/{number}/env: the world the number answers in from now on."""
184
+
185
+ env: Env
186
+
187
+
188
+ class Consent(WireModel):
189
+ """The consent a call runs on: express or written, where it came from, the words, a proof."""
190
+
191
+ kind: Literal["express", "written"]
192
+ source: str = Field(min_length=1, max_length=200)
193
+ text: str | None = Field(default=None, max_length=2000)
194
+ evidence: str | None = Field(default=None, max_length=500)
195
+
196
+
197
+ class DialRequest(WireModel):
198
+ """POST /v1/agents/{slug}/dial: the far end, the org's number it is shown, the consent."""
199
+
200
+ to: str
201
+ from_: str | None = Field(None, alias="from")
202
+ consent: Consent | None = None
203
+ # What the log token handed back reads the call through.
204
+ log: Projection = "tenant"
205
+
206
+
207
+ class LegTrunk(WireModel):
208
+ """How the worker dials a leg: the host, its transport, the pair, the number shown."""
209
+
210
+ hostname: str
211
+ transport: Literal["auto", "udp", "tcp", "tls"]
212
+ username: str
213
+ password: str
214
+ shown: str
215
+
216
+
217
+ class LegTrunkResponse(WireModel):
218
+ """GET /v1/agents/{slug}/outbound-trunk: the leg's trunk, inline, after the guards."""
219
+
220
+ trunk: LegTrunk
221
+
222
+
223
+ class ConsentRow(WireModel):
224
+ """One fact about a number: a consent or an opt-out, by whom, from what, on which call."""
225
+
226
+ kind: ConsentKind
227
+ source: str
228
+ text: str | None
229
+ evidence: str | None
230
+ given_by: str
231
+ call: str | None
232
+ given_at: float
233
+
234
+
235
+ class ConsentHistory(WireModel):
236
+ """GET /v1/org/consents/{number}: what stands for the number, and every row, newest first."""
237
+
238
+ number: str
239
+ standing: Literal["consented", "opted_out", "unknown"]
240
+ rows: list[ConsentRow]
241
+
242
+
243
+ class OptedOut(WireModel):
244
+ """A number on the org's do-not-call list: since when, from what, and who put it there."""
245
+
246
+ number: str
247
+ since: float
248
+ source: str
249
+ given_by: str
250
+
251
+
252
+ class DoNotCall(WireModel):
253
+ """GET /v1/org/dnc: the numbers whose newest row is an opt-out, newest first, a page."""
254
+
255
+ numbers: list[OptedOut]
256
+ next: str | None
257
+
258
+
259
+ class DoNotCallImport(WireModel):
260
+ """POST /v1/org/dnc: numbers the org's own list or a Registry scrub says not to call."""
261
+
262
+ numbers: list[str] = Field(min_length=1, max_length=10000)
263
+ source: str = Field(min_length=1, max_length=200)
264
+
265
+
266
+ class DoNotCallImported(WireModel):
267
+ """POST /v1/org/dnc, the answer: how many numbers joined the list, and the ones refused."""
268
+
269
+ added: int
270
+ refused: list[str]
271
+
272
+
273
+ class RecordConsent(WireModel):
274
+ """POST /v1/org/consents: one fact about a number, a consent given or an opt-out."""
275
+
276
+ number: str
277
+ kind: ConsentKind
278
+ source: str = Field(min_length=1, max_length=200)
279
+ text: str | None = Field(default=None, max_length=2000)
280
+ evidence: str | None = Field(default=None, max_length=500)
@@ -0,0 +1,342 @@
1
+ """The bodies of the box's own doors: orgs and what they hold, their people and keys, the box."""
2
+
3
+ from typing import Literal
4
+
5
+ from pydantic import Field
6
+
7
+ from pinecall.domain.names import Channel, Env
8
+ from pinecall.domain.org import QuotaName
9
+ from pinecall.wire.frames import Entry, WireModel
10
+ from pinecall.wire.rest.accounts import MemberRow
11
+ from pinecall.wire.rest.fleet import FleetTotals, WorkerStatus
12
+ from pinecall.wire.rest.numbers import DialGuards
13
+
14
+ # `bought` on the box's account; `twilio`, `sip`, `whatsapp` the org's account it was imported
15
+ # from; `imported` from an account since forgotten; `hooked` pointed at the box by the org itself;
16
+ # `typed` a row the box's operator wrote.
17
+ type NumberCameIn = Literal["bought", "twilio", "sip", "whatsapp", "imported", "hooked", "typed"]
18
+
19
+
20
+ class CreateOrgRequest(WireModel):
21
+ """POST /v1/ops/orgs: the slug, and the name people read; the slug when none."""
22
+
23
+ slug: str
24
+ name: str | None = None
25
+
26
+
27
+ class OrgRow(WireModel):
28
+ """One org: its minted id, its slug and its name."""
29
+
30
+ id: str
31
+ slug: str
32
+ name: str
33
+
34
+
35
+ # The same shape read and written: a limit left out is none, and `lends` null lends everything.
36
+ class OrgQuotas(WireModel):
37
+ """An org's limits in one world, by name; null is no limit, and `lends` comes back sorted."""
38
+
39
+ limits: dict[QuotaName, int | None] = Field(default_factory=dict[QuotaName, int | None])
40
+ budget_usd: int | None = None
41
+ lends: list[str] | None = None
42
+
43
+
44
+ class PutQuotasRequest(WireModel):
45
+ """PUT /v1/ops/orgs/{named}/quotas: which world, and the whole set for it."""
46
+
47
+ env: Env
48
+ quotas: OrgQuotas
49
+
50
+
51
+ class PutDiallingRequest(WireModel):
52
+ """PUT /v1/ops/orgs/{named}/dialling: the guards whole; one left out is the default."""
53
+
54
+ dial_anywhere: bool | None = None
55
+ per_minute: int | None = None
56
+ per_day: int | None = None
57
+ max_duration_s: int | None = None
58
+
59
+
60
+ class OrgHolding(WireModel):
61
+ """What the org holds right now, across both worlds, against its stock quotas."""
62
+
63
+ memory_facts: int
64
+ knowledge_chunks: int
65
+ numbers: int
66
+ seats: int
67
+
68
+
69
+ class OrgProfile(WireModel):
70
+ """GET /v1/ops/orgs/{named}: one org, its quotas per world, its dial guards, its holdings."""
71
+
72
+ id: str
73
+ slug: str
74
+ name: str
75
+ quotas: dict[Env, OrgQuotas]
76
+ dialling: DialGuards
77
+ holding: OrgHolding
78
+
79
+
80
+ class MoveAgentRequest(WireModel):
81
+ """PUT /v1/ops/orgs/{named}/agents: the agent to move into this org."""
82
+
83
+ agent: str
84
+
85
+
86
+ class AgentMovedResponse(WireModel):
87
+ """The move: how many logs came, which numbers came, which stayed where they were."""
88
+
89
+ agent: str
90
+ org: str
91
+ logs: int
92
+ numbers: list[str]
93
+ stayed: list[str]
94
+
95
+
96
+ class IssueKeyRequest(WireModel):
97
+ """POST /v1/ops/orgs/{named}/keys: a key of the org, in a world, with scopes and an owner."""
98
+
99
+ env: Env
100
+ label: str | None = None
101
+ scopes: list[str] | None = None
102
+ subject: str | None = None
103
+ name: str | None = None
104
+
105
+
106
+ class OrgMembersResponse(WireModel):
107
+ """GET /v1/ops/orgs/{named}/members: the org's people, and how many hold a seat."""
108
+
109
+ members: list[MemberRow]
110
+ seated: int
111
+
112
+
113
+ class OperatorRequest(WireModel):
114
+ """PUT /v1/ops/orgs/{named}/members/{id}/operator: whether this member runs the box."""
115
+
116
+ operator: bool
117
+
118
+
119
+ class SsoRequiredRequest(WireModel):
120
+ """PUT /v1/ops/orgs/{named}/sso/required: whether a password may still open the org."""
121
+
122
+ required: bool
123
+
124
+
125
+ class BoxMailResponse(WireModel):
126
+ """GET and PUT /v1/ops/mail: the box's mailbox and where it came from, never the password."""
127
+
128
+ configured: bool
129
+ source: str | None
130
+ host: str | None
131
+ port: int | None
132
+ security: str | None
133
+ username: str | None
134
+ from_: str | None = Field(alias="from")
135
+ verified_at: str | None
136
+ last_error: str | None
137
+
138
+
139
+ class BoxProvider(WireModel):
140
+ """One box-wide identity provider: whether it is wired, the client, the URI to register."""
141
+
142
+ configured: bool
143
+ client_id: str | None
144
+ redirect_uri: str
145
+
146
+
147
+ class BoxSignInResponse(WireModel):
148
+ """GET /v1/ops/signin: every provider the box could offer every org's people."""
149
+
150
+ google: BoxProvider
151
+
152
+
153
+ class PutSignInRequest(WireModel):
154
+ """PUT /v1/ops/signin/google: the OAuth client the operator made for this gateway."""
155
+
156
+ client_id: str
157
+ client_secret: str
158
+
159
+
160
+ class PutBrandRequest(WireModel):
161
+ """PUT /v1/ops/brand: a field left out keeps its value; an empty one goes to the default."""
162
+
163
+ name: str | None = None
164
+ logo_url: str | None = None
165
+ accent: str | None = None
166
+
167
+
168
+ class RouteRequest(WireModel):
169
+ """POST /v1/ops/routes: which org's agent answers a number, on a channel, in a world."""
170
+
171
+ org: str
172
+ number: str
173
+ agent: str
174
+ channel: Channel
175
+ env: Env = "production"
176
+
177
+
178
+ class RouteRow(WireModel):
179
+ """One route as the operator lists it."""
180
+
181
+ org: str
182
+ number: str
183
+ agent: str
184
+ channel: Channel
185
+ env: Env
186
+ managed: bool
187
+
188
+
189
+ class BoxNumber(WireModel):
190
+ """GET /v1/ops/numbers: a number the box answers at, how it came, and who picks it up."""
191
+
192
+ number: str
193
+ channel: Channel
194
+ org: str
195
+ env: Env
196
+ agent: str
197
+ came_in: NumberCameIn
198
+ # A process holds the agent in the org and world now: a call to the number is answered.
199
+ running: bool
200
+ # The catalog carrier a number the org hooked comes through, when it named one.
201
+ via: str | None = None
202
+ # False for a number the org hooked that waits for the operator: no call to it opens.
203
+ approved: bool = True
204
+
205
+
206
+ class BoxCarrier(WireModel):
207
+ """A carrier of the box's catalog: its published networks, and whether it is admitted."""
208
+
209
+ kind: str
210
+ name: str
211
+ # The box drives its API (lists, points, buys numbers); the others are SIP terms alone.
212
+ control: bool
213
+ networks: list[str]
214
+ source: str
215
+ read_on: str
216
+ admitted: bool
217
+ # The platform's own carrier: admitted always, its networks in the cloud firewall.
218
+ fixed: bool
219
+ # The numbers of every org that reach the box through it.
220
+ numbers: int
221
+
222
+
223
+ class FenceOpening(WireModel):
224
+ """A network 5060 opens to beyond Twilio's, and why: a carrier's kind, or an org's ask."""
225
+
226
+ network: str
227
+ reason: str
228
+
229
+
230
+ class BoxFence(WireModel):
231
+ """What the fence opens to beyond Twilio's, and every network the cloud's firewall admits."""
232
+
233
+ openings: list[FenceOpening]
234
+ # Twilio's first, then each opening: Terraform's `sip_sources` (`fence export`).
235
+ networks: list[str]
236
+
237
+
238
+ class BoxCarriers(WireModel):
239
+ """GET /v1/ops/carriers: the catalog, each carrier admitted or not, and the fence."""
240
+
241
+ carriers: list[BoxCarrier]
242
+ fence: BoxFence
243
+
244
+
245
+ class RepointRequest(WireModel):
246
+ """POST /v1/ops/sip/repoint: the names each world was reached at before, sent on from."""
247
+
248
+ former: dict[Env, list[str]] = Field(default_factory=dict[Env, list[str]])
249
+
250
+
251
+ class RepointedTrunk(WireModel):
252
+ """POST /v1/ops/sip/repoint: a trunk of an account sent on from a world's name to its SIP's."""
253
+
254
+ account: str
255
+ trunk: str
256
+ world: Env
257
+ was: str
258
+ now: str
259
+
260
+
261
+ class AdmitCarrierRequest(WireModel):
262
+ """PUT /v1/ops/carriers/{kind}: whether orgs may bring numbers through the carrier."""
263
+
264
+ admitted: bool
265
+
266
+
267
+ class CarrierNetworkRow(WireModel):
268
+ """A network an org asked 5060 to open to: who, for what, and the operator's answer."""
269
+
270
+ id: int
271
+ org: str
272
+ source: str
273
+ network: str
274
+ state: str
275
+ asked_at: float
276
+ decided_by: str | None
277
+ decided_at: float | None
278
+
279
+
280
+ class FleetListed(WireModel):
281
+ """GET /v1/ops/fleet: the box's time, every worker heard from, and each fleet summed."""
282
+
283
+ now: float
284
+ stale_after_s: float
285
+ workers: list[WorkerStatus]
286
+ totals: list[FleetTotals]
287
+
288
+
289
+ class FleetDemand(WireModel):
290
+ """GET /v1/fleet/wanted: how many scaled workers the fleet wants, for KEDA."""
291
+
292
+ fleet: str
293
+ wanted: int
294
+ # The fleet's calls and the seats its workers hold now, for whoever reads why.
295
+ active: int
296
+ seats: int
297
+
298
+
299
+ class BoxEvent(WireModel):
300
+ """One frame of GET /v1/ops/events: an entry of some org's floor, whose, and in which world."""
301
+
302
+ org: str
303
+ env: Env | None = None
304
+ entry: Entry
305
+
306
+
307
+ class TracebackCall(WireModel):
308
+ """One phone call with the number: whose, which way, when, how it ended, whether erased."""
309
+
310
+ call: str
311
+ org: str | None
312
+ env: str | None
313
+ direction: str | None
314
+ from_number: str | None
315
+ to_number: str | None
316
+ started_at: float | None
317
+ ended_at: float | None
318
+ end_reason: str | None
319
+ # The call's log is gone and only its detail record is left (0016_call_records.sql).
320
+ erased: bool
321
+
322
+
323
+ class TracebackDial(WireModel):
324
+ """One dial to the number: whose, the call it placed, the number shown, who asked, the guard."""
325
+
326
+ org: str
327
+ env: str
328
+ agent: str
329
+ call: str | None
330
+ shown: str | None
331
+ asked_by: str
332
+ refused: str | None
333
+ at: float
334
+
335
+
336
+ class Traceback(WireModel):
337
+ """GET /v1/ops/traceback: what the box keeps of one number since a day, oldest first."""
338
+
339
+ number: str
340
+ since: float
341
+ calls: list[TracebackCall]
342
+ dials: list[TracebackDial]
@@ -0,0 +1,83 @@
1
+ """The bodies of the providers doors: the catalogue, the org's own keys, the voices, a sample."""
2
+
3
+ from typing import Literal
4
+
5
+ from pinecall.domain.names import JsonObject
6
+ from pinecall.wire.frames import WireModel
7
+
8
+ type Availability = Literal["yours", "offered", "bring your own", "broken"]
9
+
10
+
11
+ type Standing = Literal["ready", "no plugin", "no key", "its own"]
12
+
13
+
14
+ # v1's row, which the console reads, and `availability` and `broken` beside it: `standing` is
15
+ # `availability` in v1's words, and the fields v1 read off a table in code are empty here.
16
+ class ProviderRow(WireModel):
17
+ """One installed vendor: what it does, whose key runs it for this org, and why not."""
18
+
19
+ name: str
20
+ does: list[Literal["llm", "stt", "tts"]]
21
+ aliases: list[str]
22
+ note: str
23
+ standing: Standing
24
+ ready: bool
25
+ env: str | None
26
+ extra: str
27
+ voices_listed: bool
28
+ availability: Availability
29
+ broken: str | None
30
+
31
+
32
+ class Catalogue(WireModel):
33
+ """GET /v1/providers: every vendor installed, the defaults per stage, the models named."""
34
+
35
+ providers: list[ProviderRow]
36
+ defaults: dict[str, str]
37
+ # The picker reads GET /v1/voices; no names are curated in code.
38
+ voices: list[str]
39
+ models: dict[str, list[str]]
40
+
41
+
42
+ class VendorsResponse(WireModel):
43
+ """GET /v1/provider-keys: the vendors the org brought its own credentials for, never one."""
44
+
45
+ vendors: list[str]
46
+
47
+
48
+ # One key, or the whole object a plugin's constructor takes (`speech_key` and `speech_region`).
49
+ class ProviderKeyRequest(WireModel):
50
+ """PUT /v1/provider-keys/{vendor}: the org's own credentials for one vendor."""
51
+
52
+ key: str | None = None
53
+ credentials: JsonObject | None = None
54
+
55
+
56
+ class ListedVoice(WireModel):
57
+ """One voice as the picker shows it: the id the `voice` setting takes, and what to choose by."""
58
+
59
+ id: str
60
+ name: str
61
+ language: str
62
+ description: str
63
+ gender: str
64
+ country: str
65
+ accent: str
66
+
67
+
68
+ class VoicesListed(WireModel):
69
+ """GET /v1/voices?tts=&language=: a vendor's own voices, in the vendor's order."""
70
+
71
+ tts: str
72
+ language: str | None
73
+ voices: list[ListedVoice]
74
+
75
+
76
+ class VoiceSampleRequest(WireModel):
77
+ """POST /v1/voices/sample: which vendor, voice, model and words to hear; the answer is WAV."""
78
+
79
+ tts: str
80
+ voice: str
81
+ model: str | None = None
82
+ language: str | None = None
83
+ text: str | None = None