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.
- pinecall/__init__.py +0 -0
- pinecall/channels/__init__.py +0 -0
- pinecall/channels/_chooser.py +45 -0
- pinecall/channels/offers.py +219 -0
- pinecall/channels/rooms.py +176 -0
- pinecall/channels/routes.py +275 -0
- pinecall/channels/telephony/__init__.py +0 -0
- pinecall/channels/telephony/_twilio.py +349 -0
- pinecall/channels/telephony/carrier.py +229 -0
- pinecall/channels/telephony/carrier_catalog.py +92 -0
- pinecall/channels/telephony/carriers.csv +41 -0
- pinecall/channels/telephony/dialing.py +419 -0
- pinecall/channels/telephony/firewall.py +58 -0
- pinecall/channels/telephony/hand_over.py +78 -0
- pinecall/channels/telephony/number_path.py +176 -0
- pinecall/channels/telephony/numbers.py +530 -0
- pinecall/channels/telephony/origination.py +77 -0
- pinecall/channels/telephony/sip.py +448 -0
- pinecall/channels/whatsapp.py +413 -0
- pinecall/cli/__init__.py +0 -0
- pinecall/cli/_archive.py +85 -0
- pinecall/cli/_drift.py +38 -0
- pinecall/cli/_facts.py +67 -0
- pinecall/cli/_load.py +433 -0
- pinecall/cli/_operator.py +626 -0
- pinecall/cli/_sessions.py +164 -0
- pinecall/cli/_traceback.py +95 -0
- pinecall/cli/_usage.py +34 -0
- pinecall/cli/main.py +521 -0
- pinecall/domain/__init__.py +0 -0
- pinecall/domain/agent.py +446 -0
- pinecall/domain/call.py +152 -0
- pinecall/domain/country_codes.py +32 -0
- pinecall/domain/errors.py +151 -0
- pinecall/domain/names.py +115 -0
- pinecall/domain/org.py +120 -0
- pinecall/domain/person.py +208 -0
- pinecall/domain/scope.py +21 -0
- pinecall/evals/__init__.py +0 -0
- pinecall/evals/_evidence.py +158 -0
- pinecall/evals/calibration.py +112 -0
- pinecall/evals/callers.py +160 -0
- pinecall/evals/case.py +327 -0
- pinecall/evals/checks.py +437 -0
- pinecall/evals/compliance.py +197 -0
- pinecall/evals/dataset.py +451 -0
- pinecall/evals/goldens.py +331 -0
- pinecall/evals/judges.py +525 -0
- pinecall/evals/runs.py +252 -0
- pinecall/evals/spoken.py +356 -0
- pinecall/fleet/__init__.py +0 -0
- pinecall/fleet/client.py +569 -0
- pinecall/fleet/demand.py +51 -0
- pinecall/fleet/heartbeat.py +194 -0
- pinecall/fleet/measures.py +157 -0
- pinecall/fleet/roster.py +232 -0
- pinecall/fleet/worlds.py +44 -0
- pinecall/gateway/__init__.py +0 -0
- pinecall/gateway/_call_setup.py +53 -0
- pinecall/gateway/_deps.py +556 -0
- pinecall/gateway/_gateway.py +75 -0
- pinecall/gateway/_served.py +396 -0
- pinecall/gateway/_sockets.py +444 -0
- pinecall/gateway/_streams.py +83 -0
- pinecall/gateway/_text_calls.py +182 -0
- pinecall/gateway/api/__init__.py +0 -0
- pinecall/gateway/api/accounts.py +324 -0
- pinecall/gateway/api/agents.py +150 -0
- pinecall/gateway/api/apps.py +478 -0
- pinecall/gateway/api/box.py +658 -0
- pinecall/gateway/api/calibration.py +58 -0
- pinecall/gateway/api/callbacks.py +105 -0
- pinecall/gateway/api/calls.py +655 -0
- pinecall/gateway/api/chat.py +223 -0
- pinecall/gateway/api/dataset.py +163 -0
- pinecall/gateway/api/desk.py +113 -0
- pinecall/gateway/api/entries.py +77 -0
- pinecall/gateway/api/evals.py +647 -0
- pinecall/gateway/api/fleet.py +73 -0
- pinecall/gateway/api/hosting.py +241 -0
- pinecall/gateway/api/judges.py +97 -0
- pinecall/gateway/api/keys.py +110 -0
- pinecall/gateway/api/line.py +142 -0
- pinecall/gateway/api/members.py +197 -0
- pinecall/gateway/api/metrics.py +226 -0
- pinecall/gateway/api/numbers.py +476 -0
- pinecall/gateway/api/ops.py +428 -0
- pinecall/gateway/api/org.py +283 -0
- pinecall/gateway/api/personas.py +157 -0
- pinecall/gateway/api/pipeline.py +206 -0
- pinecall/gateway/api/prompts.py +50 -0
- pinecall/gateway/api/providers.py +240 -0
- pinecall/gateway/api/recordings.py +89 -0
- pinecall/gateway/api/relay.py +169 -0
- pinecall/gateway/api/retrieval.py +403 -0
- pinecall/gateway/api/runner.py +109 -0
- pinecall/gateway/api/sessions.py +102 -0
- pinecall/gateway/api/settings.py +463 -0
- pinecall/gateway/api/signup.py +164 -0
- pinecall/gateway/api/sso_login.py +168 -0
- pinecall/gateway/api/threads.py +187 -0
- pinecall/gateway/api/usage.py +303 -0
- pinecall/gateway/api/visitors.py +222 -0
- pinecall/gateway/api/whatsapp.py +48 -0
- pinecall/gateway/api/widget.py +49 -0
- pinecall/gateway/app.py +504 -0
- pinecall/gateway/calls/__init__.py +0 -0
- pinecall/gateway/calls/binding.py +63 -0
- pinecall/gateway/calls/commands.py +18 -0
- pinecall/gateway/calls/inbox.py +100 -0
- pinecall/gateway/calls/known.py +23 -0
- pinecall/gateway/calls/owners.py +87 -0
- pinecall/gateway/calls/pump.py +33 -0
- pinecall/gateway/calls/serving.py +118 -0
- pinecall/gateway/calls/threads.py +367 -0
- pinecall/gateway/dispatching/__init__.py +0 -0
- pinecall/gateway/dispatching/arrivals.py +58 -0
- pinecall/gateway/dispatching/sweep.py +43 -0
- pinecall/gateway/ending/__init__.py +0 -0
- pinecall/gateway/ending/reaper.py +160 -0
- pinecall/gateway/ending/seal.py +312 -0
- pinecall/gateway/ending/stranded.py +117 -0
- pinecall/log/__init__.py +0 -0
- pinecall/log/_histogram.py +74 -0
- pinecall/log/_relay.py +259 -0
- pinecall/log/_writer.py +454 -0
- pinecall/log/days.py +154 -0
- pinecall/log/drift.py +550 -0
- pinecall/log/facts.py +358 -0
- pinecall/log/inbox.py +169 -0
- pinecall/log/lists.py +155 -0
- pinecall/log/logs.py +581 -0
- pinecall/log/openings.py +71 -0
- pinecall/log/private.py +135 -0
- pinecall/log/queries.py +229 -0
- pinecall/log/readers.py +207 -0
- pinecall/log/reduce.py +659 -0
- pinecall/log/refold.py +217 -0
- pinecall/log/store.py +370 -0
- pinecall/log/usage.py +242 -0
- pinecall/postgres/__init__.py +0 -0
- pinecall/postgres/migrate.py +202 -0
- pinecall/postgres/migrations/0001_schema.sql +591 -0
- pinecall/postgres/migrations/0002_worlds.sql +15 -0
- pinecall/postgres/migrations/0003_lent.sql +4 -0
- pinecall/postgres/migrations/0004_carriers.sql +23 -0
- pinecall/postgres/migrations/0005_retrieved_only.sql +11 -0
- pinecall/postgres/migrations/0006_persona_agents.sql +9 -0
- pinecall/postgres/migrations/0007_money_in_dollars.sql +27 -0
- pinecall/postgres/migrations/0008_lexicon_per_agent.sql +27 -0
- pinecall/postgres/migrations/0009_personas_per_agent.sql +31 -0
- pinecall/postgres/migrations/0010_agent_judges.sql +12 -0
- pinecall/postgres/migrations/0011_erasure.sql +32 -0
- pinecall/postgres/migrations/0012_org_policy.sql +10 -0
- pinecall/postgres/migrations/0013_calling_rules.sql +12 -0
- pinecall/postgres/migrations/0014_consents.sql +24 -0
- pinecall/postgres/migrations/0015_disclosure.sql +7 -0
- pinecall/postgres/migrations/0016_call_records.sql +21 -0
- pinecall/postgres/migrations/0017_reads.sql +17 -0
- pinecall/postgres/migrations/0018_hosting.sql +41 -0
- pinecall/postgres/migrations/0019_hosted_state.sql +10 -0
- pinecall/postgres/migrations/0020_written_once.sql +7 -0
- pinecall/postgres/migrations/0021_hosted_running.sql +21 -0
- pinecall/postgres/migrations/0022_sealed.sql +39 -0
- pinecall/postgres/migrations/0023_usage_totals.sql +158 -0
- pinecall/postgres/migrations/0024_log_key_by_day.sql +8 -0
- pinecall/postgres/migrations/0025_log_before_the_days.sql +15 -0
- pinecall/postgres/migrations/0026_log_before_the_days_validated.sql +4 -0
- pinecall/postgres/migrations/0027_log_by_day.sql +96 -0
- pinecall/postgres/migrations/0030_whatsapp_seen.sql +17 -0
- pinecall/postgres/migrations/0040_reads_widened.sql +15 -0
- pinecall/postgres/migrations/0060_drift.sql +62 -0
- pinecall/postgres/migrations/0061_canaries.sql +24 -0
- pinecall/postgres/migrations/0062_eval_cases.sql +23 -0
- pinecall/postgres/migrations/0063_prompts.sql +16 -0
- pinecall/postgres/migrations/0075_call_private.sql +14 -0
- pinecall/postgres/migrations/0076_recording_keys.sql +13 -0
- pinecall/postgres/migrations/0077_tokens_world.sql +7 -0
- pinecall/postgres/migrations/0078_facts_cost_by_stage.sql +12 -0
- pinecall/postgres/migrations/0079_judge_labels.sql +20 -0
- pinecall/postgres/migrations/0080_call_openings.sql +20 -0
- pinecall/postgres/migrations/0081_seal_lease.sql +7 -0
- pinecall/postgres/migrations/0082_caller_codes.sql +14 -0
- pinecall/postgres/migrations/0083_one_use_words.sql +15 -0
- pinecall/postgres/migrations/0084_knocks_and_run_leases.sql +23 -0
- pinecall/postgres/migrations/0085_seal_lease_holder.sql +8 -0
- pinecall/postgres/migrations/0086_route_origin.sql +17 -0
- pinecall/postgres/migrations/0087_carrier_networks.sql +20 -0
- pinecall/postgres/migrations/0088_route_last_call.sql +3 -0
- pinecall/postgres/migrations/0089_offers.sql +21 -0
- pinecall/postgres/migrations/0090_invitation_purpose.sql +5 -0
- pinecall/postgres/migrations/0091_head_opened_by.sql +6 -0
- pinecall/postgres/migrations/0092_key_parent.sql +6 -0
- pinecall/postgres/migrations/0093_code_tries.sql +10 -0
- pinecall/postgres/migrations/0094_route_approval.sql +12 -0
- pinecall/postgres/migrations/0095_sso_domain_proofs.sql +17 -0
- pinecall/postgres/migrations/0096_row_level_security.sql +38 -0
- pinecall/postgres/migrations/0097_case_status.sql +19 -0
- pinecall/postgres/pool.py +190 -0
- pinecall/process/__init__.py +0 -0
- pinecall/process/_objects.py +97 -0
- pinecall/process/box_settings.py +23 -0
- pinecall/process/connections.py +124 -0
- pinecall/process/metrics.py +127 -0
- pinecall/process/recordings.py +323 -0
- pinecall/process/resolver.py +41 -0
- pinecall/process/sealed_audio.py +173 -0
- pinecall/process/settings.py +534 -0
- pinecall/process/shared.py +158 -0
- pinecall/process/signal.py +416 -0
- pinecall/providers/__init__.py +0 -0
- pinecall/providers/build.py +570 -0
- pinecall/providers/catalog.py +266 -0
- pinecall/providers/credentials.py +230 -0
- pinecall/providers/declared.py +69 -0
- pinecall/providers/prices.py +240 -0
- pinecall/providers/tried.py +104 -0
- pinecall/providers/voices.py +174 -0
- pinecall/public/console/assets/index-BIfKPSgn.js +164 -0
- pinecall/public/console/assets/index-ClOTU-xX.css +1 -0
- pinecall/public/console/favicon.png +0 -0
- pinecall/public/console/index.html +20 -0
- pinecall/public/console/logo-mark.png +0 -0
- pinecall/public/console/pinecall-logo-dark.png +0 -0
- pinecall/public/console/pinecall-logo.png +0 -0
- pinecall/public/console/pinecall-mark.png +0 -0
- pinecall/public/console/sw.js +37 -0
- pinecall/public/widget/pinecall-widget.js +853 -0
- pinecall/py.typed +0 -0
- pinecall/retrieval/__init__.py +0 -0
- pinecall/retrieval/_search.py +186 -0
- pinecall/retrieval/_sections.py +70 -0
- pinecall/retrieval/embed.py +340 -0
- pinecall/retrieval/extraction.py +403 -0
- pinecall/retrieval/knowledge.py +659 -0
- pinecall/retrieval/lookups.py +192 -0
- pinecall/retrieval/memory.py +603 -0
- pinecall/runner/__init__.py +0 -0
- pinecall/runner/_kube.py +459 -0
- pinecall/runner/_plan.py +157 -0
- pinecall/runner/main.py +306 -0
- pinecall/session/__init__.py +0 -0
- pinecall/session/_agent.py +141 -0
- pinecall/session/_hearing.py +101 -0
- pinecall/session/_livekit.py +195 -0
- pinecall/session/_prompt.py +147 -0
- pinecall/session/_recording.py +237 -0
- pinecall/session/call.py +367 -0
- pinecall/session/clock.py +40 -0
- pinecall/session/hold.py +239 -0
- pinecall/session/room.py +626 -0
- pinecall/session/session.py +690 -0
- pinecall/session/text.py +144 -0
- pinecall/session/tools.py +483 -0
- pinecall/session/voice.py +118 -0
- pinecall/session/widget.py +201 -0
- pinecall/tenancy/__init__.py +0 -0
- pinecall/tenancy/admission.py +204 -0
- pinecall/tenancy/agents.py +164 -0
- pinecall/tenancy/canary.py +126 -0
- pinecall/tenancy/carrier_networks.py +156 -0
- pinecall/tenancy/carriers.py +265 -0
- pinecall/tenancy/codes.py +196 -0
- pinecall/tenancy/consents.py +167 -0
- pinecall/tenancy/dial_policy.py +405 -0
- pinecall/tenancy/disclosure.py +41 -0
- pinecall/tenancy/erasure.py +204 -0
- pinecall/tenancy/export.py +167 -0
- pinecall/tenancy/hosted_running.py +166 -0
- pinecall/tenancy/hosting.py +475 -0
- pinecall/tenancy/judges.py +126 -0
- pinecall/tenancy/keys.py +467 -0
- pinecall/tenancy/knocks.py +40 -0
- pinecall/tenancy/letters.py +378 -0
- pinecall/tenancy/mail.py +458 -0
- pinecall/tenancy/org_secrets.py +123 -0
- pinecall/tenancy/orgs.py +78 -0
- pinecall/tenancy/people.py +543 -0
- pinecall/tenancy/personas.py +145 -0
- pinecall/tenancy/policy.py +65 -0
- pinecall/tenancy/prompts.py +48 -0
- pinecall/tenancy/reads.py +73 -0
- pinecall/tenancy/recording_keys.py +41 -0
- pinecall/tenancy/remembered.py +148 -0
- pinecall/tenancy/retention.py +86 -0
- pinecall/tenancy/scopes.py +406 -0
- pinecall/tenancy/signin.py +622 -0
- pinecall/tenancy/spend.py +69 -0
- pinecall/tenancy/sso.py +431 -0
- pinecall/tenancy/sso_domains.py +132 -0
- pinecall/tenancy/throttle.py +88 -0
- pinecall/tenancy/tokens.py +400 -0
- pinecall/tenancy/traceback.py +70 -0
- pinecall/tenancy/usage.py +136 -0
- pinecall/tenancy/vault.py +204 -0
- pinecall/tenancy/words.py +144 -0
- pinecall/wire/__init__.py +0 -0
- pinecall/wire/commands.py +350 -0
- pinecall/wire/events.py +655 -0
- pinecall/wire/frames.py +65 -0
- pinecall/wire/metrics.py +287 -0
- pinecall/wire/parts.py +320 -0
- pinecall/wire/rest/__init__.py +0 -0
- pinecall/wire/rest/accounts.py +512 -0
- pinecall/wire/rest/agents.py +172 -0
- pinecall/wire/rest/calls.py +397 -0
- pinecall/wire/rest/evals.py +453 -0
- pinecall/wire/rest/fleet.py +69 -0
- pinecall/wire/rest/hosting.py +174 -0
- pinecall/wire/rest/numbers.py +280 -0
- pinecall/wire/rest/ops.py +342 -0
- pinecall/wire/rest/providers.py +83 -0
- pinecall/wire/rest/retrieval.py +301 -0
- pinecall/wire/rest/settings.py +184 -0
- pinecall/wire/rest/usage.py +245 -0
- pinecall/wire/room.py +61 -0
- pinecall/wire/scores.py +49 -0
- pinecall/wire/state.py +243 -0
- pinecall/worker/__init__.py +0 -0
- pinecall/worker/_job.py +655 -0
- pinecall/worker/_recorder.py +82 -0
- pinecall/worker/_traces.py +48 -0
- pinecall/worker/main.py +307 -0
- pinecall_runtime-0.1.6.dist-info/METADATA +212 -0
- pinecall_runtime-0.1.6.dist-info/RECORD +328 -0
- pinecall_runtime-0.1.6.dist-info/WHEEL +4 -0
- pinecall_runtime-0.1.6.dist-info/entry_points.txt +2 -0
- pinecall_runtime-0.1.6.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
-- Drift: each day's stage latencies and judges' verdicts, by agent and by the config version a
|
|
2
|
+
-- call ran on, folded once when the call is sealed (log/drift.py), so /v1/insights and
|
|
3
|
+
-- /v1/insights/drift read a few rows instead of a day of the log.
|
|
4
|
+
--
|
|
5
|
+
-- stage_days keeps a stage's seconds as a fixed histogram (log/_histogram.py: 94 buckets, each 10 %
|
|
6
|
+
-- wider than the last): two calls' histograms add bucket by bucket, and a median or p95 read off
|
|
7
|
+
-- one is within 5 %. judge_days counts each judge's settled verdicts under the hash of the
|
|
8
|
+
-- question it asked. config_version 0 is a call that ran on no version of its scope's settings;
|
|
9
|
+
-- vendor and model '' are a turn whose report named none, as the PRIMARY KEY cannot hold a null.
|
|
10
|
+
-- drift_calls is one row per call folded: it keeps the fold from counting a call twice, and holds
|
|
11
|
+
-- the verdicts it counted, so a call judged again replaces them rather than adding.
|
|
12
|
+
--
|
|
13
|
+
-- Three new tables: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for
|
|
14
|
+
-- each foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
15
|
+
|
|
16
|
+
CREATE TABLE stage_days (
|
|
17
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
18
|
+
env text NOT NULL CHECK (env IN ('production', 'sandbox')),
|
|
19
|
+
holder text NOT NULL,
|
|
20
|
+
agent text NOT NULL,
|
|
21
|
+
day date NOT NULL,
|
|
22
|
+
config_version integer NOT NULL,
|
|
23
|
+
stage text NOT NULL CHECK (stage IN ('stt', 'llm', 'tts')),
|
|
24
|
+
vendor text NOT NULL,
|
|
25
|
+
model text NOT NULL,
|
|
26
|
+
turns integer NOT NULL,
|
|
27
|
+
buckets integer[] NOT NULL,
|
|
28
|
+
confidence_sum double precision NOT NULL,
|
|
29
|
+
confidence_turns integer NOT NULL,
|
|
30
|
+
PRIMARY KEY (org, env, holder, day, agent, config_version, stage, vendor, model)
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
CREATE TABLE judge_days (
|
|
34
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
35
|
+
env text NOT NULL CHECK (env IN ('production', 'sandbox')),
|
|
36
|
+
holder text NOT NULL,
|
|
37
|
+
agent text NOT NULL,
|
|
38
|
+
day date NOT NULL,
|
|
39
|
+
config_version integer NOT NULL,
|
|
40
|
+
judge text NOT NULL,
|
|
41
|
+
criteria text NOT NULL,
|
|
42
|
+
held integer NOT NULL,
|
|
43
|
+
broken integer NOT NULL,
|
|
44
|
+
PRIMARY KEY (org, env, holder, day, agent, config_version, judge, criteria)
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
-- A version's rows across its days, for a drift between two versions.
|
|
48
|
+
CREATE INDEX stage_days_by_version ON stage_days (org, env, holder, agent, config_version);
|
|
49
|
+
CREATE INDEX judge_days_by_version ON judge_days (org, env, holder, agent, config_version);
|
|
50
|
+
|
|
51
|
+
CREATE TABLE drift_calls (
|
|
52
|
+
call text PRIMARY KEY,
|
|
53
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
54
|
+
env text NOT NULL,
|
|
55
|
+
holder text NOT NULL,
|
|
56
|
+
agent text NOT NULL,
|
|
57
|
+
day date NOT NULL,
|
|
58
|
+
config_version integer NOT NULL,
|
|
59
|
+
verdicts jsonb NOT NULL
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
CREATE INDEX drift_calls_by_day ON drift_calls (org, day);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- A version of an agent's settings on a share of its calls (tenancy/canary.py): the newest row of
|
|
2
|
+
-- a scope says which version takes `share` % of the scope's calls, each call picked once by its
|
|
3
|
+
-- id; every other call runs what the scope would run without that version. A row with no version
|
|
4
|
+
-- is the canary cleared. Rows are only ever added, so the canary that stood when a call opened is
|
|
5
|
+
-- read back for it (GET /v1/calls/{call}/settings says whether the call was one).
|
|
6
|
+
--
|
|
7
|
+
-- A new table: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for the
|
|
8
|
+
-- foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
9
|
+
|
|
10
|
+
CREATE TABLE agent_canaries (
|
|
11
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
12
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
13
|
+
env text NOT NULL CHECK (env IN ('production', 'sandbox')),
|
|
14
|
+
holder text NOT NULL,
|
|
15
|
+
agent text NOT NULL,
|
|
16
|
+
version integer CHECK (version > 0),
|
|
17
|
+
share integer CHECK (share BETWEEN 0 AND 100),
|
|
18
|
+
author text NOT NULL,
|
|
19
|
+
note text,
|
|
20
|
+
set_at timestamptz NOT NULL DEFAULT now(),
|
|
21
|
+
CHECK ((version IS NULL) = (share IS NULL))
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
CREATE INDEX agent_canaries_standing ON agent_canaries (org, env, holder, agent, set_at DESC, id DESC);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- The org's dataset: a real call's caller turns kept as a golden an eval run can play again
|
|
2
|
+
-- (evals/dataset.py). A case is tenant data like the call it came from: erasing that call, the
|
|
3
|
+
-- contact who made it, or the org erases the case too (tenancy/erasure.py by `source_call`, the
|
|
4
|
+
-- foreign key for the org), and the calls retention erases go through the same path.
|
|
5
|
+
--
|
|
6
|
+
-- A new table: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for the
|
|
7
|
+
-- foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
8
|
+
|
|
9
|
+
CREATE TABLE eval_cases (
|
|
10
|
+
id text PRIMARY KEY,
|
|
11
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
12
|
+
agent text NOT NULL,
|
|
13
|
+
name text NOT NULL,
|
|
14
|
+
golden jsonb NOT NULL,
|
|
15
|
+
source_call text NOT NULL,
|
|
16
|
+
source_env text NOT NULL CHECK (source_env IN ('production', 'sandbox')),
|
|
17
|
+
held_out boolean NOT NULL DEFAULT false,
|
|
18
|
+
author text NOT NULL,
|
|
19
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
20
|
+
UNIQUE (org, agent, name)
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX eval_cases_by_call ON eval_cases (source_call);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
-- Each distinct block of prompt an org's calls were told, kept once under the hash the log's
|
|
2
|
+
-- prompt.changed names (tenancy/prompts.py), so a call of thirty days ago has its exact prompt:
|
|
3
|
+
-- the knowledge its settings placed, and every block its app set. Written the first time a
|
|
4
|
+
-- gateway process meets a text (a call opening with its knowledge, an app's prompt.set), never
|
|
5
|
+
-- once per call. Tenant data: the org's foreign key takes it with the org.
|
|
6
|
+
--
|
|
7
|
+
-- A new table: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for the
|
|
8
|
+
-- foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
9
|
+
|
|
10
|
+
CREATE TABLE prompts (
|
|
11
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
12
|
+
hash text NOT NULL,
|
|
13
|
+
text text NOT NULL,
|
|
14
|
+
first_used_at timestamptz NOT NULL DEFAULT now(),
|
|
15
|
+
PRIMARY KEY (org, hash)
|
|
16
|
+
);
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- What is private is sealed when written. An entry whose agent declared some of its values
|
|
2
|
+
-- private (a tool's `pii` arguments, a state field declared `pii`) is kept in call_log with
|
|
3
|
+
-- those values masked, and the values themselves are kept here, sealed under PINECALL_VAULT_KEY,
|
|
4
|
+
-- one row per entry, keyed by the entry's log and seq (log/private.py). They are read only where
|
|
5
|
+
-- a call is taken up: an app taking it over, a written call after a restart. `vault rotate`
|
|
6
|
+
-- re-seals the column; an erasure deletes the rows of the logs it erases (tenancy/erasure.py).
|
|
7
|
+
-- A new table with no foreign key: CREATE TABLE takes no lock on any table that exists, so
|
|
8
|
+
-- appends and reads go on while it runs.
|
|
9
|
+
CREATE TABLE call_private (
|
|
10
|
+
log text NOT NULL,
|
|
11
|
+
seq bigint NOT NULL,
|
|
12
|
+
sealed text NOT NULL,
|
|
13
|
+
PRIMARY KEY (log, seq)
|
|
14
|
+
);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- A recording is sealed where it is kept: the worker seals the file under a key of the call's own
|
|
2
|
+
-- before it stores it on the disk or in the bucket, and the gateway opens a player's range with
|
|
3
|
+
-- it (process/sealed_audio.py). The key is kept here, sealed under PINECALL_VAULT_KEY, one row per
|
|
4
|
+
-- call, made the first time the call's worker asks (tenancy/recording_keys.py); `vault rotate`
|
|
5
|
+
-- re-seals it, and an erasure deletes it with the call, so a copy of the file that outlives the
|
|
6
|
+
-- erasure (a night's backup) no longer opens. A recording stored before this is plain, and named so.
|
|
7
|
+
-- A new table with no foreign key: CREATE TABLE takes no lock on any table that exists.
|
|
8
|
+
CREATE TABLE recording_keys (
|
|
9
|
+
call text PRIMARY KEY,
|
|
10
|
+
org text NOT NULL,
|
|
11
|
+
sealed text NOT NULL,
|
|
12
|
+
made_at timestamptz NOT NULL DEFAULT now()
|
|
13
|
+
);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- A web call is opened only where its token was minted: the org, the agent and now the world.
|
|
2
|
+
-- The world a room token was minted in is kept with it, and the open compares it with the world
|
|
3
|
+
-- the worker names (tenancy/tokens.py spend). A token minted before this has none, and is held to
|
|
4
|
+
-- its org and agent alone; it expires within minutes of its mint in any case.
|
|
5
|
+
-- ADD COLUMN with no default and no NOT NULL changes the catalog alone: ACCESS EXCLUSIVE on tokens
|
|
6
|
+
-- for the instant of the statement, no rewrite; a mint or an open waits that instant.
|
|
7
|
+
ALTER TABLE tokens ADD COLUMN env text;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- A call's cost by stage: what its model, ears, voice, phone legs and the box's own compute came
|
|
2
|
+
-- to, folded from the summary's rows beside cost_usd (log/facts.py), so a day's insights say which
|
|
3
|
+
-- stage an agent spends on without reading a summary. A call folded before this is filled in by
|
|
4
|
+
-- `pinecall-runtime facts rebuild`.
|
|
5
|
+
-- ADD COLUMN with no default and no NOT NULL changes the catalog alone: ACCESS EXCLUSIVE on
|
|
6
|
+
-- call_facts for the instant of the statement, no rewrite; a fold waits that instant.
|
|
7
|
+
ALTER TABLE call_facts
|
|
8
|
+
ADD COLUMN cost_llm_usd double precision,
|
|
9
|
+
ADD COLUMN cost_stt_usd double precision,
|
|
10
|
+
ADD COLUMN cost_tts_usd double precision,
|
|
11
|
+
ADD COLUMN cost_phone_usd double precision,
|
|
12
|
+
ADD COLUMN cost_platform_usd double precision;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- What a person said a judge should have answered on a call (evals/calibration.py): one label per
|
|
2
|
+
-- call and judge, replaced when labelled again. Each judge's agreement with them is read against
|
|
3
|
+
-- the verdict the seal counted (drift_calls), so a judge that disagrees with the people who know
|
|
4
|
+
-- is reported, never trusted silently. A label goes with its call's erasure and with the org.
|
|
5
|
+
-- A new table: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for the
|
|
6
|
+
-- foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
7
|
+
CREATE TABLE judge_labels (
|
|
8
|
+
call text NOT NULL,
|
|
9
|
+
judge text NOT NULL,
|
|
10
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
11
|
+
env text NOT NULL CHECK (env IN ('production', 'sandbox')),
|
|
12
|
+
agent text NOT NULL,
|
|
13
|
+
held boolean NOT NULL,
|
|
14
|
+
note text,
|
|
15
|
+
author text NOT NULL,
|
|
16
|
+
labelled_at timestamptz NOT NULL DEFAULT now(),
|
|
17
|
+
PRIMARY KEY (call, judge)
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE INDEX judge_labels_by_org ON judge_labels (org, env, agent);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- What a call is, kept when it opens (log/openings.py): its context and the tuned config it runs
|
|
2
|
+
-- on, so a gateway that did not open it serves its doors from one read instead of from the
|
|
3
|
+
-- worker saying the call again. A config is kept once per org under the hash of its JSON: most
|
|
4
|
+
-- calls of an agent run on a handful. An opening goes with its call's erasure; both with the org.
|
|
5
|
+
-- New tables: CREATE TABLE locks nothing that exists but `orgs`, in SHARE ROW EXCLUSIVE for each
|
|
6
|
+
-- foreign key, for the instant of the statement: reads of orgs go on, a write to it waits.
|
|
7
|
+
CREATE TABLE call_configs (
|
|
8
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
9
|
+
hash text NOT NULL,
|
|
10
|
+
config jsonb NOT NULL,
|
|
11
|
+
PRIMARY KEY (org, hash)
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE TABLE call_openings (
|
|
15
|
+
call text PRIMARY KEY,
|
|
16
|
+
org text NOT NULL REFERENCES orgs(id) ON DELETE CASCADE,
|
|
17
|
+
context jsonb NOT NULL,
|
|
18
|
+
config_hash text NOT NULL,
|
|
19
|
+
opened_at timestamptz NOT NULL DEFAULT now()
|
|
20
|
+
);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
-- The seal's lease: a call is sealed by one gateway, whichever the worker's seal reached first.
|
|
2
|
+
-- The gateway that takes the lease (log/store.py `lease_seal`) prices, remembers, judges and seals;
|
|
3
|
+
-- one that finds it taken waits for the head to say sealed. A lease runs out on its own, so a
|
|
4
|
+
-- gateway that died mid-seal leaves the call to the next knock.
|
|
5
|
+
-- ADD COLUMN with no default and no NOT NULL changes the catalog alone: ACCESS EXCLUSIVE on
|
|
6
|
+
-- call_log_head for the instant of the statement, no rewrite; an append waits that instant.
|
|
7
|
+
ALTER TABLE call_log_head ADD COLUMN sealing_until timestamptz;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
-- The four-digit codes a caller keys to tie their call to a page (tenancy/codes.py), kept here so
|
|
2
|
+
-- any gateway issues, claims and answers them: before, each gateway held its own in memory and a
|
|
3
|
+
-- code issued on one was unknown to another. The agent's log keeps code.issued and code.claimed
|
|
4
|
+
-- as the record; this table is what is live. A code is claimed by one call, in one statement.
|
|
5
|
+
-- A new table: CREATE TABLE locks nothing that exists.
|
|
6
|
+
CREATE TABLE caller_codes (
|
|
7
|
+
agent text NOT NULL,
|
|
8
|
+
code text NOT NULL,
|
|
9
|
+
env text NOT NULL CHECK (env IN ('production', 'sandbox')),
|
|
10
|
+
log text NOT NULL CHECK (log IN ('public', 'tenant')),
|
|
11
|
+
expires_at double precision NOT NULL,
|
|
12
|
+
claimed text,
|
|
13
|
+
PRIMARY KEY (agent, code)
|
|
14
|
+
);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Words spent once, kept here so a word minted on one gateway is spent on any (tenancy/words.py):
|
|
2
|
+
-- a sign-in code, a terminal waiting for a browser, a provider's handshake, a sign-up waiting for
|
|
3
|
+
-- its six digits. Before, each gateway held its own in memory. A word is kept as its sha256 and
|
|
4
|
+
-- its value sealed under the vault: a copy of this table opens nothing, and a pairing's key is
|
|
5
|
+
-- never in the clear. Swept as words are minted.
|
|
6
|
+
-- A new table: CREATE TABLE locks nothing that exists.
|
|
7
|
+
CREATE TABLE one_use_words (
|
|
8
|
+
word_hash bytea PRIMARY KEY,
|
|
9
|
+
kind text NOT NULL,
|
|
10
|
+
sealed text NOT NULL,
|
|
11
|
+
expires_at double precision NOT NULL,
|
|
12
|
+
attempts integer NOT NULL DEFAULT 0
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
CREATE INDEX one_use_words_by_end ON one_use_words (expires_at);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- What must be counted or held across the gateways of a box, in the one place they share.
|
|
2
|
+
--
|
|
3
|
+
-- knocks: each time a name knocked at a sign-in door or asked for a voice sample
|
|
4
|
+
-- (tenancy/knocks.py). Five knocks a minute must mean five, whichever gateway each lands on;
|
|
5
|
+
-- these doors are rare, so a row and a lock each cost nothing that matters. Rows past the window
|
|
6
|
+
-- are deleted as knocks come in.
|
|
7
|
+
--
|
|
8
|
+
-- run_leases: the eval run each agent is under (evals/runs.py). A run takes its agent's lease and
|
|
9
|
+
-- renews it while it runs; a gateway that dies mid-run leaves a lease that runs out on its own.
|
|
10
|
+
-- New tables: CREATE TABLE locks nothing that exists.
|
|
11
|
+
CREATE TABLE knocks (
|
|
12
|
+
name text NOT NULL,
|
|
13
|
+
at double precision NOT NULL
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
CREATE INDEX knocks_by_name ON knocks (name, at);
|
|
17
|
+
CREATE INDEX knocks_by_age ON knocks (at);
|
|
18
|
+
|
|
19
|
+
CREATE TABLE run_leases (
|
|
20
|
+
agent text PRIMARY KEY,
|
|
21
|
+
run text NOT NULL,
|
|
22
|
+
until timestamptz NOT NULL
|
|
23
|
+
);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- Who holds the seal's lease: the gateway process that took it (log/store.py `lease_seal`), so
|
|
2
|
+
-- only it renews the lease while it seals, and only it gives the lease back after a seal that
|
|
3
|
+
-- broke. The lease is short (seal.py LEASED_S) and renewed: a gateway that dies mid-seal lets the
|
|
4
|
+
-- call go in seconds, and a knock waiting on it takes the seal over; a gateway that lost the race
|
|
5
|
+
-- cannot keep the winner's lease alive by renewing it.
|
|
6
|
+
-- ADD COLUMN with no default and no NOT NULL changes the catalog alone: ACCESS EXCLUSIVE on
|
|
7
|
+
-- call_log_head for the instant of the statement, no rewrite; an append waits that instant.
|
|
8
|
+
ALTER TABLE call_log_head ADD COLUMN sealing_by text;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- How a number's row was written, so the org and the operator can tell whom to ask when it stops
|
|
2
|
+
-- ringing: bought by the box, imported from an account of the org, hooked by the org itself
|
|
3
|
+
-- (pointed at the box from its own carrier), or typed by the box's operator.
|
|
4
|
+
-- The default is the operator's: a release before this one writes rows only through
|
|
5
|
+
-- POST /v1/ops/routes or an import, and an import written by it during the deploy reads `typed`
|
|
6
|
+
-- until the org imports it again.
|
|
7
|
+
ALTER TABLE routes ADD COLUMN origin text NOT NULL DEFAULT 'typed'
|
|
8
|
+
CHECK (origin IN ('bought', 'imported', 'hooked', 'typed'));
|
|
9
|
+
|
|
10
|
+
-- The rows already written, from what each one kept: a row with no account, no networks and not
|
|
11
|
+
-- bought was typed (every such row on the box predates the cutover of 2026-09-29).
|
|
12
|
+
UPDATE routes SET origin = CASE
|
|
13
|
+
WHEN managed THEN 'bought'
|
|
14
|
+
WHEN account IS NOT NULL THEN 'imported'
|
|
15
|
+
WHEN cardinality(networks) > 0 THEN 'hooked'
|
|
16
|
+
ELSE 'typed'
|
|
17
|
+
END;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- The networks an org asks 5060 to open to: its own PBX's, or a number it points at the box from a
|
|
2
|
+
-- carrier the box does not know. Nothing is admitted until the box's operator approves it; the
|
|
3
|
+
-- fence (channels/telephony/firewall.py) opens to the approved rows alone.
|
|
4
|
+
CREATE TABLE carrier_networks (
|
|
5
|
+
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
6
|
+
org text NOT NULL REFERENCES orgs (id) ON DELETE CASCADE,
|
|
7
|
+
-- What asked for it: a SIP peer's username, or the number hooked with networks of its own.
|
|
8
|
+
source text NOT NULL,
|
|
9
|
+
network cidr NOT NULL,
|
|
10
|
+
state text NOT NULL DEFAULT 'waiting' CHECK (state IN ('waiting', 'approved', 'refused')),
|
|
11
|
+
asked_at timestamptz NOT NULL DEFAULT now(),
|
|
12
|
+
decided_by text,
|
|
13
|
+
decided_at timestamptz,
|
|
14
|
+
UNIQUE (org, source, network)
|
|
15
|
+
);
|
|
16
|
+
CREATE INDEX carrier_networks_by_state ON carrier_networks (state, asked_at);
|
|
17
|
+
|
|
18
|
+
-- A number the org points at the box from a carrier of the box's catalog (carriers.csv): the
|
|
19
|
+
-- carrier's own networks fence it, whatever they are on the day of the call.
|
|
20
|
+
ALTER TABLE routes ADD COLUMN via text;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
-- A room with a caller and no worker yet, and the worker the gateway offered it to
|
|
2
|
+
-- (gateway/dispatching/). LiveKit offers a job once and remembers nothing: a worker that declines
|
|
3
|
+
-- it, or a dead one LiveKit still lists, leaves the caller in silence. The gateway keeps the room
|
|
4
|
+
-- here from the moment a caller joins until a worker opens its call (the row is then deleted),
|
|
5
|
+
-- offers it to a worker it chose, and offers it again to another when nobody opened it in time.
|
|
6
|
+
-- Any gateway may sweep it: an offer is taken by the UPDATE that finds the count it read, so two
|
|
7
|
+
-- gateways never offer one room twice.
|
|
8
|
+
-- New table: CREATE TABLE locks nothing that exists.
|
|
9
|
+
CREATE TABLE offers (
|
|
10
|
+
room text PRIMARY KEY,
|
|
11
|
+
fleet text NOT NULL,
|
|
12
|
+
-- The call's dispatch, as the SIP rule or the visitor's token carried it into the room.
|
|
13
|
+
dispatch text NOT NULL,
|
|
14
|
+
seen_at double precision NOT NULL,
|
|
15
|
+
worker text,
|
|
16
|
+
offers integer NOT NULL DEFAULT 0,
|
|
17
|
+
offered_at double precision
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
CREATE INDEX offers_due ON offers (offered_at);
|
|
21
|
+
CREATE INDEX offers_by_age ON offers (seen_at);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
-- A link's purpose (tenancy/people.py): an invitation seats a person and never sets a password
|
|
2
|
+
-- they already have; a reset is made to set it again. Until now one link did both, so a link
|
|
3
|
+
-- handed out to seat somebody could set their one password in every org they belong to.
|
|
4
|
+
-- New column with a default: nothing that exists stops reading.
|
|
5
|
+
ALTER TABLE invitations ADD COLUMN purpose text NOT NULL DEFAULT 'invite';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- Who opened a call's log (log/store.py Claim): the fleet's worker, an org's own worker (an app
|
|
2
|
+
-- key), or the gateway itself for a written call. The worker doors of a call the fleet opened
|
|
3
|
+
-- take the fleet's key alone; a written call's take nobody's. NULL is a call opened before this
|
|
4
|
+
-- column, served as before.
|
|
5
|
+
-- New column, nullable: nothing that exists stops reading.
|
|
6
|
+
ALTER TABLE call_log_head ADD COLUMN opened_by text;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- The key a key was copied from (tenancy/signin.py sign_in_with_code: a login code a key minted
|
|
2
|
+
-- signs a browser in with a copy of it). Revoking a key revokes every copy made of it, and every
|
|
3
|
+
-- copy of those: a leaked key can no longer outlive its revocation through a copy.
|
|
4
|
+
-- New column, nullable: nothing that exists stops reading.
|
|
5
|
+
ALTER TABLE api_keys ADD COLUMN parent text;
|
|
6
|
+
CREATE INDEX api_keys_parent ON api_keys (parent) WHERE parent IS NOT NULL;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
-- How many codes each call keyed (tenancy/codes.py): a call tries three, then no page is tied to
|
|
2
|
+
-- it, whatever it keys. Kept here so the count holds whichever gateway serves the call; a row
|
|
3
|
+
-- leaves a day after its call's first try.
|
|
4
|
+
-- A new table: CREATE TABLE locks nothing that exists.
|
|
5
|
+
CREATE TABLE code_tries (
|
|
6
|
+
call text PRIMARY KEY,
|
|
7
|
+
tries integer NOT NULL,
|
|
8
|
+
first_at double precision NOT NULL
|
|
9
|
+
);
|
|
10
|
+
CREATE INDEX code_tries_first_at ON code_tries (first_at);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
-- A number the org hooked itself (pointed at the box from its own carrier) proves nothing about
|
|
2
|
+
-- whose it is, so the operator approves it before a call to it is opened (channels/routes.py);
|
|
3
|
+
-- a number bought, imported from an account of the org or typed by the operator is approved as
|
|
4
|
+
-- it is written. Every row already written keeps answering: it reads approved at this migration.
|
|
5
|
+
-- New columns with a default: a release before this one writes rows that read approved, as it
|
|
6
|
+
-- answered them.
|
|
7
|
+
ALTER TABLE routes ADD COLUMN approved_at timestamptz DEFAULT now();
|
|
8
|
+
ALTER TABLE routes ADD COLUMN approved_by text;
|
|
9
|
+
|
|
10
|
+
-- One org holds a number: a second org's row for it was "the older one answers". Refused here if
|
|
11
|
+
-- the box holds a number twice; the operator lets one of them go first (GET /v1/ops/numbers).
|
|
12
|
+
CREATE UNIQUE INDEX routes_number_held_once ON routes (number);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- A domain an org's SSO admits is proven the org's by a TXT record at the domain
|
|
2
|
+
-- (tenancy/sso.py): the token here, published as `pinecall-verify=<token>`, read back by the
|
|
3
|
+
-- verify door. Discovery offers an org's provider for a verified domain alone, and so does the
|
|
4
|
+
-- callback. Every domain already declared is verified here: the orgs that set SSO before this
|
|
5
|
+
-- migration are the box's own, and their sign-in keeps working.
|
|
6
|
+
-- A new table: CREATE TABLE locks nothing that exists.
|
|
7
|
+
CREATE TABLE sso_domain_proofs (
|
|
8
|
+
org text NOT NULL,
|
|
9
|
+
domain text NOT NULL,
|
|
10
|
+
token text NOT NULL,
|
|
11
|
+
asked_at timestamptz NOT NULL DEFAULT now(),
|
|
12
|
+
verified_at timestamptz,
|
|
13
|
+
PRIMARY KEY (org, domain)
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
INSERT INTO sso_domain_proofs (org, domain, token, verified_at)
|
|
17
|
+
SELECT org, unnest(domains), 'before-proofs', now() FROM org_sso;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
-- Row-level security: a connection that names an org (`pinecall.org`, postgres/pool.py, set by
|
|
2
|
+
-- the gateway for a tenant's request) sees and writes that org's rows alone, whatever a query
|
|
3
|
+
-- forgot to say; one that names none is the box's, as the operator's doors, the loops and the
|
|
4
|
+
-- nightly jobs are. Every table with an `org` column, and `orgs` by its id. The owner (the
|
|
5
|
+
-- migrations, the retention) is not held to it, as Postgres never holds a table's owner.
|
|
6
|
+
-- A log's head is made before its call names an org (the seal's lease, a seal), so a head with no
|
|
7
|
+
-- org yet is anybody's to see and write: it holds an id and its flags, nothing of a tenant's.
|
|
8
|
+
-- ENABLE takes a brief ACCESS EXCLUSIVE lock per table, CREATE POLICY the same; no row is read.
|
|
9
|
+
DO $$
|
|
10
|
+
DECLARE
|
|
11
|
+
found record;
|
|
12
|
+
scoped text;
|
|
13
|
+
BEGIN
|
|
14
|
+
FOR found IN
|
|
15
|
+
SELECT class.relname AS name, CASE WHEN class.relname = 'orgs' THEN 'id' ELSE 'org' END AS col
|
|
16
|
+
FROM pg_class AS class
|
|
17
|
+
JOIN pg_namespace AS space ON space.oid = class.relnamespace
|
|
18
|
+
WHERE space.nspname = current_schema()
|
|
19
|
+
AND class.relkind IN ('r', 'p')
|
|
20
|
+
AND NOT class.relispartition
|
|
21
|
+
AND (class.relname = 'orgs' OR EXISTS (
|
|
22
|
+
SELECT 1 FROM pg_attribute AS attr
|
|
23
|
+
WHERE attr.attrelid = class.oid AND attr.attname = 'org' AND NOT attr.attisdropped))
|
|
24
|
+
LOOP
|
|
25
|
+
scoped := format(
|
|
26
|
+
'coalesce(current_setting(''pinecall.org'', true), '''') = '''' '
|
|
27
|
+
'OR %I = current_setting(''pinecall.org'', true)',
|
|
28
|
+
found.col
|
|
29
|
+
);
|
|
30
|
+
IF found.name = 'call_log_head' THEN
|
|
31
|
+
scoped := 'org IS NULL OR ' || scoped;
|
|
32
|
+
END IF;
|
|
33
|
+
EXECUTE format('ALTER TABLE %I ENABLE ROW LEVEL SECURITY', found.name);
|
|
34
|
+
EXECUTE format(
|
|
35
|
+
'CREATE POLICY org_scoped ON %I USING (%s) WITH CHECK (%s)', found.name, scoped, scoped
|
|
36
|
+
);
|
|
37
|
+
END LOOP;
|
|
38
|
+
END $$;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
-- A case is born at hang-up when a judge broke (evals/dataset.py `kept_at_hangup`), so it waits
|
|
2
|
+
-- for a person: `pending` until approved into the nightly or dismissed. The cases that exist were
|
|
3
|
+
-- kept by hand and stay `approved`. `broke` is what the panel said, `source_version` the settings
|
|
4
|
+
-- version the call ran on, `kept_in_repo` a case written into the repository as a golden, which
|
|
5
|
+
-- the nightly then leaves to the file.
|
|
6
|
+
--
|
|
7
|
+
-- Expanding only: each column has a constant default (no rewrite), the check is read over a
|
|
8
|
+
-- table of a few rows per org, and the index is new.
|
|
9
|
+
|
|
10
|
+
ALTER TABLE eval_cases
|
|
11
|
+
ADD COLUMN status text NOT NULL DEFAULT 'approved'
|
|
12
|
+
CHECK (status IN ('pending', 'approved', 'dismissed')),
|
|
13
|
+
ADD COLUMN broke jsonb NOT NULL DEFAULT '[]',
|
|
14
|
+
ADD COLUMN source_version integer,
|
|
15
|
+
ADD COLUMN kept_in_repo boolean NOT NULL DEFAULT false,
|
|
16
|
+
ADD COLUMN decided_by text,
|
|
17
|
+
ADD COLUMN decided_at timestamptz;
|
|
18
|
+
|
|
19
|
+
CREATE INDEX eval_cases_pending ON eval_cases (org, agent) WHERE status = 'pending';
|