@open-everywhere/adapters 0.7.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/agent/aws/sso.ts +0 -2
  2. package/agent/instrumentation.ts +67 -4
  3. package/agent/model/approval-judge.ts +3 -3
  4. package/agent/model/dream-judge.ts +8 -6
  5. package/agent/model/gateway-probe.ts +97 -11
  6. package/agent/model/gateway-vision.ts +10 -6
  7. package/agent/model/gateway.ts +448 -34
  8. package/agent/model/meeting-gateway.ts +37 -7
  9. package/agent/model/model-config-check.ts +243 -0
  10. package/agent/model/target-url.ts +38 -0
  11. package/agent/model/transcription-routes.ts +18 -4
  12. package/agent/oe-oauth/errors.ts +1 -1
  13. package/agent/oe-oauth/token-vault.ts +99 -29
  14. package/agent/oe-oauth/user-auth-flow.ts +4 -3
  15. package/agent/operator-holders.ts +47 -0
  16. package/agent/postgres/neon.ts +1 -1
  17. package/dist/agent/aws/sso.d.ts +1 -2
  18. package/dist/agent/aws/sso.d.ts.map +1 -1
  19. package/dist/agent/aws/sso.js +0 -2
  20. package/dist/agent/aws/sso.js.map +1 -1
  21. package/dist/agent/instrumentation.d.ts +23 -1
  22. package/dist/agent/instrumentation.d.ts.map +1 -1
  23. package/dist/agent/instrumentation.js +59 -3
  24. package/dist/agent/instrumentation.js.map +1 -1
  25. package/dist/agent/model/approval-judge.d.ts.map +1 -1
  26. package/dist/agent/model/approval-judge.js +3 -3
  27. package/dist/agent/model/approval-judge.js.map +1 -1
  28. package/dist/agent/model/dream-judge.d.ts +2 -0
  29. package/dist/agent/model/dream-judge.d.ts.map +1 -1
  30. package/dist/agent/model/dream-judge.js +6 -4
  31. package/dist/agent/model/dream-judge.js.map +1 -1
  32. package/dist/agent/model/gateway-probe.d.ts +22 -4
  33. package/dist/agent/model/gateway-probe.d.ts.map +1 -1
  34. package/dist/agent/model/gateway-probe.js +78 -11
  35. package/dist/agent/model/gateway-probe.js.map +1 -1
  36. package/dist/agent/model/gateway-vision.d.ts +4 -2
  37. package/dist/agent/model/gateway-vision.d.ts.map +1 -1
  38. package/dist/agent/model/gateway-vision.js +6 -4
  39. package/dist/agent/model/gateway-vision.js.map +1 -1
  40. package/dist/agent/model/gateway.d.ts +85 -12
  41. package/dist/agent/model/gateway.d.ts.map +1 -1
  42. package/dist/agent/model/gateway.js +418 -30
  43. package/dist/agent/model/gateway.js.map +1 -1
  44. package/dist/agent/model/meeting-gateway.d.ts +8 -2
  45. package/dist/agent/model/meeting-gateway.d.ts.map +1 -1
  46. package/dist/agent/model/meeting-gateway.js +29 -7
  47. package/dist/agent/model/meeting-gateway.js.map +1 -1
  48. package/dist/agent/model/model-config-check.d.ts +57 -0
  49. package/dist/agent/model/model-config-check.d.ts.map +1 -0
  50. package/dist/agent/model/model-config-check.js +207 -0
  51. package/dist/agent/model/model-config-check.js.map +1 -0
  52. package/dist/agent/model/target-url.d.ts +10 -0
  53. package/dist/agent/model/target-url.d.ts.map +1 -0
  54. package/dist/agent/model/target-url.js +32 -0
  55. package/dist/agent/model/target-url.js.map +1 -0
  56. package/dist/agent/model/transcription-routes.d.ts +3 -0
  57. package/dist/agent/model/transcription-routes.d.ts.map +1 -1
  58. package/dist/agent/model/transcription-routes.js +16 -4
  59. package/dist/agent/model/transcription-routes.js.map +1 -1
  60. package/dist/agent/oe-oauth/errors.d.ts +0 -7
  61. package/dist/agent/oe-oauth/errors.d.ts.map +1 -1
  62. package/dist/agent/oe-oauth/errors.js +1 -1
  63. package/dist/agent/oe-oauth/errors.js.map +1 -1
  64. package/dist/agent/oe-oauth/token-vault.d.ts +4 -3
  65. package/dist/agent/oe-oauth/token-vault.d.ts.map +1 -1
  66. package/dist/agent/oe-oauth/token-vault.js +88 -24
  67. package/dist/agent/oe-oauth/token-vault.js.map +1 -1
  68. package/dist/agent/oe-oauth/user-auth-flow.d.ts.map +1 -1
  69. package/dist/agent/oe-oauth/user-auth-flow.js +3 -2
  70. package/dist/agent/oe-oauth/user-auth-flow.js.map +1 -1
  71. package/dist/agent/operator-holders.d.ts +13 -0
  72. package/dist/agent/operator-holders.d.ts.map +1 -0
  73. package/dist/agent/operator-holders.js +40 -0
  74. package/dist/agent/operator-holders.js.map +1 -0
  75. package/dist/agent/postgres/neon.d.ts +0 -15
  76. package/dist/agent/postgres/neon.d.ts.map +1 -1
  77. package/dist/agent/postgres/neon.js +1 -1
  78. package/dist/agent/postgres/neon.js.map +1 -1
  79. package/package.json +19 -2
@@ -0,0 +1,243 @@
1
+ // THE LIVE MODEL CONFIG CHECK (OPE-2556, the OPE-2535 ticket's comment #2). It answers, BEFORE a call fails, whether
2
+ // the deployment's model config can be served: for each model the caller lists (the stack roles, the gateway
3
+ // transcription routes, the domain and role routes, `models.force`), one GET of the gateway's
4
+ // public `/v1/models/<id>/endpoints` read, and three questions:
5
+ //
6
+ // ① does the model exist on the gateway?
7
+ // ② does at least one `providers.only` provider serve it? (A slug that serves none of the checked models gets a
8
+ // warning, because it is most likely a typo. It is not a failure: every call can still be served.)
9
+ // ③ does at least one allowed provider meet EVERY `models.dataPolicy` row at once (`has_zdr`, `has_no_training`,
10
+ // `inference_regions`)?
11
+ //
12
+ // A row no provider can meet is named before deploy, for example "region eu: no provider for deepseek/…".
13
+ //
14
+ // 🔴 THE ENDPOINT FIELDS ARE UNDOCUMENTED, so a missing field is UNKNOWN, never a pass. One rule reads more than a
15
+ // field: `inference_regions`. Most endpoints omit it. When at least one endpoint of a model lists regions, an endpoint
16
+ // that omits them is read as serving none of the listed regions: that is how the gateway answered on 2026-10-14
17
+ // ("No provider for deepseek/deepseek-v4-flash-0731 supports the requested inference region", where only some
18
+ // endpoints listed `us` and none listed `eu`). When no endpoint lists regions, the region row is unknown.
19
+ //
20
+ // It needs the network, so it is never an offline gate. Three readers run it: the deployment status probe
21
+ // (`model-route`, gateway-probe.ts), which `create --check --host` and the setup skill read, and
22
+ // `npm run check:model-config`, which runs it from the app (a clone or a scaffolded app) before a deploy.
23
+
24
+ import { type ModelDataPolicy, type ModelTarget, mergeDataPolicy } from "@open-everywhere/core/agent/adapter-api.js";
25
+
26
+ /** The default origin of the Vercel AI Gateway's public model reads. */
27
+ const DEFAULT_GATEWAY_ORIGIN = "https://ai-gateway.vercel.sh";
28
+
29
+ export type CheckVerdict = "yes" | "no" | "unknown";
30
+
31
+ /** One endpoint (one provider serving one model), as the check reads it. `undefined` is an unknown field. */
32
+ export interface EndpointFacts {
33
+ readonly provider: string;
34
+ readonly zdr: boolean | undefined;
35
+ readonly noTraining: boolean | undefined;
36
+ /** The regions the endpoint lists, or undefined when the endpoint omits the field. */
37
+ readonly regions: readonly string[] | undefined;
38
+ }
39
+
40
+ /** A model the deployment can resolve, and the target its calls go through. */
41
+ export interface CheckedModel {
42
+ readonly id: string;
43
+ /** The `models.targets` entry (absent: `gateway`). */
44
+ readonly via?: string;
45
+ }
46
+
47
+ /** One problem or gap the check found. `row` names the demand in the settings file's words. */
48
+ export interface ConfigFinding {
49
+ readonly met: Exclude<CheckVerdict, "yes">;
50
+ readonly model?: string;
51
+ readonly row: string;
52
+ readonly detail: string;
53
+ }
54
+
55
+ export interface ConfigCheckResult {
56
+ /** "no" when any finding is "no"; "unknown" when any is "unknown"; else "yes". */
57
+ readonly met: CheckVerdict;
58
+ readonly findings: readonly ConfigFinding[];
59
+ /** How many distinct (target, model) pairs were read. */
60
+ readonly checked: number;
61
+ }
62
+
63
+ /** The endpoints read for one model id under a target's base URL (or the default origin). The id keeps its slash. */
64
+ export function modelEndpointsUrl(id: string, baseUrl?: string): string {
65
+ let origin = DEFAULT_GATEWAY_ORIGIN;
66
+ if (baseUrl !== undefined) {
67
+ try {
68
+ origin = new URL(baseUrl).origin;
69
+ } catch {
70
+ // The settings validator refuses an unparseable url; the default origin keeps the check honest.
71
+ }
72
+ }
73
+ return `${origin}/v1/models/${id.split("/").map(encodeURIComponent).join("/")}/endpoints`;
74
+ }
75
+
76
+ const asBool = (v: unknown): boolean | undefined => (typeof v === "boolean" ? v : undefined);
77
+
78
+ /** Parse an `/endpoints` body into facts, or undefined when the body is not one. Pure. */
79
+ export function parseEndpoints(body: unknown): EndpointFacts[] | undefined {
80
+ const eps = (body as { data?: { endpoints?: unknown } } | null)?.data?.endpoints;
81
+ if (!Array.isArray(eps)) return undefined;
82
+ const out: EndpointFacts[] = [];
83
+ for (const e of eps) {
84
+ if (e === null || typeof e !== "object") continue;
85
+ const r = e as Record<string, unknown>;
86
+ if (typeof r["provider_name"] !== "string") continue;
87
+ const regions = Array.isArray(r["inference_regions"])
88
+ ? (r["inference_regions"] as unknown[]).flatMap((x) => {
89
+ const g = (x as { geo_region?: unknown } | null)?.geo_region;
90
+ return typeof g === "string" ? [g] : [];
91
+ })
92
+ : undefined;
93
+ out.push({ provider: r["provider_name"], zdr: asBool(r["has_zdr"]), noTraining: asBool(r["has_no_training"]), regions });
94
+ }
95
+ return out;
96
+ }
97
+
98
+ const and = (a: CheckVerdict, b: CheckVerdict): CheckVerdict => (a === "no" || b === "no" ? "no" : a === "unknown" || b === "unknown" ? "unknown" : "yes");
99
+ /** At least one provider: "yes" when one is, "no" when every one is not (or there is none), else "unknown". */
100
+ const any = (vs: readonly CheckVerdict[]): CheckVerdict => (vs.includes("yes") ? "yes" : vs.every((v) => v === "no") ? "no" : "unknown");
101
+ const fromBool = (b: boolean | undefined): CheckVerdict => (b === undefined ? "unknown" : b ? "yes" : "no");
102
+
103
+ /** The policy's rows as (label, per-endpoint verdict) pairs. */
104
+ function rowJudges(policy: ModelDataPolicy, eps: readonly EndpointFacts[]): { label: string; judge: (e: EndpointFacts) => CheckVerdict }[] {
105
+ const rows: { label: string; judge: (e: EndpointFacts) => CheckVerdict }[] = [];
106
+ if (policy.zeroRetention === true) rows.push({ label: "zeroRetention", judge: (e) => fromBool(e.zdr) });
107
+ if (policy.noTraining === true) rows.push({ label: "noTraining", judge: (e) => fromBool(e.noTraining) });
108
+ const region = policy.region;
109
+ if (region !== undefined) {
110
+ const listed = eps.some((e) => e.regions !== undefined);
111
+ rows.push({ label: `region ${region}`, judge: (e) => (e.regions !== undefined ? fromBool(e.regions.includes(region)) : listed ? "no" : "unknown") });
112
+ }
113
+ return rows;
114
+ }
115
+
116
+ /**
117
+ * Judge one model's endpoints against a target and the policy. Pure. Returns the findings ("no" and "unknown" only),
118
+ * so an empty list means every demand is met.
119
+ */
120
+ export function judgeModelEndpoints(id: string, eps: readonly EndpointFacts[], target: ModelTarget, policy: ModelDataPolicy, via = "gateway"): ConfigFinding[] {
121
+ const findings: ConfigFinding[] = [];
122
+ const only = target.providers?.only;
123
+ const candidates = only === undefined ? eps : eps.filter((e) => only.includes(e.provider));
124
+ if (only !== undefined && candidates.length === 0) {
125
+ findings.push({ met: "no", model: id, row: `models.targets.${via}.providers.only`, detail: `providers.only: no allowed provider serves ${id}` });
126
+ return findings;
127
+ }
128
+ // The target's own `dataPolicy` rows join the deployment's (OPE-2531). Two different regions cannot both be met.
129
+ let merged: ModelDataPolicy;
130
+ try {
131
+ merged = mergeDataPolicy(policy, target.dataPolicy);
132
+ } catch {
133
+ findings.push({ met: "no", model: id, row: `models.targets.${via}.dataPolicy region`, detail: `region: models.dataPolicy and target ${via} name two different regions` });
134
+ return findings;
135
+ }
136
+ // The region evidence is read over ALL the model's endpoints: whether the gateway lists regions for this model at all.
137
+ const rows = rowJudges(merged, eps);
138
+ if (rows.length === 0) return findings;
139
+ const joint = any(candidates.map((e) => rows.reduce<CheckVerdict>((v, r) => and(v, r.judge(e)), "yes")));
140
+ if (joint === "yes") return findings;
141
+ // Name each row no provider can meet on its own; when every row is met by SOME provider, name them together.
142
+ const alone = rows.map((r) => ({ label: r.label, v: any(candidates.map(r.judge)) }));
143
+ const failing = alone.filter((r) => r.v === joint);
144
+ const named = failing.length > 0 ? failing.map((r) => r.label) : [rows.map((r) => r.label).join(" + ")];
145
+ // A row the deployment's own policy does not set came from the target: name the target's row (OPE-2531).
146
+ const ownRows = rowJudges(policy, eps).map((r) => r.label);
147
+ for (const label of named) {
148
+ findings.push({
149
+ met: joint,
150
+ model: id,
151
+ row: label
152
+ .split(" + ")
153
+ .map((l) => (ownRows.includes(l) ? `models.dataPolicy ${l}` : `models.targets.${via}.dataPolicy ${l}`))
154
+ .join(" + "),
155
+ detail: joint === "no" ? `${label}: no provider for ${id}` : `${label}: the gateway does not say whether a provider of ${id} meets it`,
156
+ });
157
+ }
158
+ return findings;
159
+ }
160
+
161
+ export interface ConfigCheckInput {
162
+ readonly models: readonly CheckedModel[];
163
+ readonly targets: Readonly<Record<string, ModelTarget>>;
164
+ readonly policy: ModelDataPolicy;
165
+ /** The base URL each target resolves to (absent: the default origin). */
166
+ readonly baseUrlFor: (via: string) => string | undefined;
167
+ readonly fetchImpl?: typeof globalThis.fetch;
168
+ /** One budget for every read. */
169
+ readonly timeoutMs?: number;
170
+ }
171
+
172
+ /**
173
+ * Run the check: one GET per distinct (target, model), in parallel, under one time budget. FAIL-SOFT: a read that
174
+ * fails or times out is "unknown" for that model, never a pass and never a throw. No vendor error text is forwarded.
175
+ */
176
+ export async function checkModelConfig(input: ConfigCheckInput): Promise<ConfigCheckResult> {
177
+ const fetchImpl = input.fetchImpl ?? globalThis.fetch;
178
+ const seen = new Map<string, CheckedModel & { via: string }>();
179
+ for (const m of input.models) {
180
+ const via = m.via ?? "gateway";
181
+ seen.set(`${via}\u0000${m.id}`, { id: m.id, via });
182
+ }
183
+ const controller = new AbortController();
184
+ const fuse = setTimeout(() => controller.abort(), input.timeoutMs ?? 5_000);
185
+ const servedBy = new Map<string, Set<string>>(); // via → providers that serve at least one checked model
186
+ try {
187
+ const perModel = await Promise.all(
188
+ [...seen.values()].map(async (m): Promise<ConfigFinding[]> => {
189
+ const target = input.targets[m.via];
190
+ if (target === undefined) return [{ met: "no", model: m.id, row: `via ${m.via}`, detail: `via ${m.via}: models.targets names no such target` }];
191
+ let res: Response;
192
+ try {
193
+ res = await fetchImpl(modelEndpointsUrl(m.id, input.baseUrlFor(m.via)), { method: "GET", signal: controller.signal });
194
+ } catch {
195
+ return [{ met: "unknown", model: m.id, row: "endpoints", detail: `${m.id}: the gateway did not answer the endpoints read` }];
196
+ }
197
+ // A retargeted target (a relay) owes nobody this read: its 404 or 405 means "not served here", not "no such model".
198
+ const retargeted = input.baseUrlFor(m.via) !== undefined;
199
+ if (retargeted && (res.status === 404 || res.status === 405)) {
200
+ return [{ met: "unknown", model: m.id, row: "endpoints", detail: `${m.id}: the target ${m.via} does not serve the endpoints read` }];
201
+ }
202
+ if (res.status === 404) return [{ met: "no", model: m.id, row: "model id", detail: `${m.id}: the gateway does not know this model` }];
203
+ if (!res.ok) return [{ met: "unknown", model: m.id, row: "endpoints", detail: `${m.id}: the endpoints read answered HTTP ${res.status}` }];
204
+ let eps: EndpointFacts[] | undefined;
205
+ try {
206
+ eps = parseEndpoints(await res.json());
207
+ } catch {
208
+ eps = undefined;
209
+ }
210
+ if (eps === undefined) return [{ met: "unknown", model: m.id, row: "endpoints", detail: `${m.id}: the endpoints read is not in the expected shape` }];
211
+ const served = servedBy.get(m.via) ?? new Set<string>();
212
+ for (const e of eps) served.add(e.provider);
213
+ servedBy.set(m.via, served);
214
+ return judgeModelEndpoints(m.id, eps, target, input.policy, m.via);
215
+ }),
216
+ );
217
+ const findings = perModel.flat();
218
+ // A `providers.only` slug that serves none of the target's checked models: most likely a typo. It is a WARNING
219
+ // ("unknown"), never a "no": every call can still be served, and a deployer may list a provider for a later stack.
220
+ // Only judged when every model of the target was read, so a failed read never blames a slug.
221
+ for (const [via, served] of servedBy) {
222
+ const readAll = ![...seen.values()].some((m) => m.via === via && findings.some((f) => f.model === m.id && f.row === "endpoints"));
223
+ if (!readAll) continue;
224
+ for (const slug of input.targets[via]?.providers?.only ?? []) {
225
+ if (!served.has(slug)) findings.push({ met: "unknown", row: `models.targets.${via}.providers.only`, detail: `providers.only: "${slug}" serves none of the checked models` });
226
+ }
227
+ }
228
+ const met: CheckVerdict = findings.some((f) => f.met === "no") ? "no" : findings.length > 0 ? "unknown" : "yes";
229
+ return { met, findings, checked: seen.size };
230
+ } finally {
231
+ clearTimeout(fuse);
232
+ }
233
+ }
234
+
235
+ /** The result in one short line a person reads: "4 models checked, no problem", or the problems by name. Pure. */
236
+ export function configCheckSummary(r: ConfigCheckResult): string {
237
+ const plural = r.checked === 1 ? "model" : "models";
238
+ if (r.findings.length === 0) return `model config: ${r.checked} ${plural} checked, every demand can be served`;
239
+ const no = r.findings.filter((f) => f.met === "no");
240
+ const shown = (no.length > 0 ? no : r.findings).map((f) => f.detail);
241
+ const head = no.length > 0 ? `${no.length} problem${no.length === 1 ? "" : "s"}` : "not fully measured";
242
+ return `model config: ${head} — ${shown.slice(0, 3).join("; ")}${shown.length > 3 ? `; and ${shown.length - 3} more` : ""}`;
243
+ }
@@ -0,0 +1,38 @@
1
+ // THE BASE URL OF A MODEL TARGET (OPE-2556) — `models.targets.<name>.url`, with the deprecated `OE_AI_GATEWAY_URL`
2
+ // as the `gateway` target's fallback for one release. Its own module so the model adapter (gateway.ts) and the
3
+ // status probe (gateway-probe.ts) resolve the same URL without the probe loading the adapter.
4
+
5
+ import { DEFAULT_MODEL_VIA, type ModelTarget } from "@open-everywhere/core/agent/adapter-api.js";
6
+
7
+ /** The deprecated env var that retargets the `gateway` target (OPE-2556: `models.targets.gateway.url` replaces it). */
8
+ export const DEPRECATED_GATEWAY_URL_ENV = "OE_AI_GATEWAY_URL";
9
+
10
+ const warned = new Set<string>();
11
+ function warnOnce(key: string, message: string): void {
12
+ if (warned.has(key)) return;
13
+ warned.add(key);
14
+ console.warn(message);
15
+ }
16
+
17
+ /**
18
+ * THE BASE URL OF ONE TARGET. The target's `url` wins. The deprecated `OE_AI_GATEWAY_URL` still retargets the
19
+ * `gateway` target when its `url` is unset, for one release, with a warning. Absent both: the kind's default
20
+ * endpoint (`undefined`). Pure but for the warnings; exported for tests.
21
+ */
22
+ export function targetBaseUrl(name: string, t: ModelTarget, env: NodeJS.ProcessEnv = process.env): string | undefined {
23
+ const fromEnv = name === DEFAULT_MODEL_VIA ? env[DEPRECATED_GATEWAY_URL_ENV]?.trim() || undefined : undefined;
24
+ if (t.url !== undefined) {
25
+ if (fromEnv !== undefined && fromEnv !== t.url) {
26
+ warnOnce("env-ignored", `[open-everywhere:model] ${DEPRECATED_GATEWAY_URL_ENV} is set and ignored: models.targets.${name}.url wins. Unset the env var.`);
27
+ }
28
+ return t.url;
29
+ }
30
+ if (fromEnv !== undefined) {
31
+ warnOnce(
32
+ "env-deprecated",
33
+ `[open-everywhere:model] ${DEPRECATED_GATEWAY_URL_ENV} is deprecated and goes away in the next release. Move it to models.targets.${name}.url in agent/settings/settings.ts.`,
34
+ );
35
+ }
36
+ return fromEnv;
37
+ }
38
+
@@ -18,12 +18,17 @@
18
18
  // provider allow-list, a timeout) tries the fallback route. Both fail ⇒ it throws one error that names both. It
19
19
  // never tries a third route: the stack names what may hear the meeting, and nothing else may.
20
20
  //
21
+ // 🔴 THE DATA POLICY (OPE-2535). A `gateway` route carries `models.dataPolicy` on every call (gateway.ts). The
22
+ // `deepinfra` route cannot carry it, so with any row set it REFUSES before a byte leaves, and the fallback rule
23
+ // above does not let the audio go anywhere the policy did not allow. A `self-hosted` route is the deployer's own
24
+ // server, so the policy's provider rows do not apply to it.
25
+ //
21
26
  // 🔴 THE DISCLOSURE IS DERIVED. The approval card shows {@link roleDisclosure}, built from the SAME route list the
22
27
  // call walks, so the card cannot name one provider while the audio goes to another.
23
28
 
24
29
  import { experimental_transcribe as transcribe } from "ai";
25
- import { getOperatorSecret } from "@open-everywhere/core/agent/adapter-api.js";
26
- import { GATEWAY_TARGET_HOST, gatewayTranscriptionModel, sharedHostPlaneFetch } from "./gateway.js";
30
+ import { type ModelDataPolicy, dataPolicyActive, dataPolicyRows, getOperatorSecret, getSettings } from "@open-everywhere/core/agent/adapter-api.js";
31
+ import { gatewayTargetHost, gatewayTranscriptionModel, sharedHostPlaneFetch } from "./gateway.js";
27
32
 
28
33
  /** How a route reaches its model. */
29
34
  export type TranscriptionVia = "gateway" | "deepinfra" | "self-hosted";
@@ -82,6 +87,8 @@ export interface TranscriptionDeps {
82
87
  readonly attemptTimeoutMs?: number;
83
88
  /** Where a fall-through is reported. Default: `console.warn`. */
84
89
  readonly warn?: (message: string) => void;
90
+ /** The deployment's data policy, read per call. Default: `models.dataPolicy` from the settings. */
91
+ readonly dataPolicy?: () => ModelDataPolicy;
85
92
  }
86
93
 
87
94
  /** The secret-store name of the DeepInfra key. A fixed name in code, so no config line can point it at another secret. */
@@ -259,7 +266,7 @@ function selfHostedCall(f: typeof fetch) {
259
266
  * union and neither the turn model nor a transcription route drops the other's host.
260
267
  */
261
268
  export function roleHostPlaneHosts(role: TranscriptionRoleSpec): string[] {
262
- const hosts = [GATEWAY_TARGET_HOST];
269
+ const hosts = [gatewayTargetHost()];
263
270
  for (const r of [role.primary, role.fallback]) {
264
271
  if (r === undefined || r.via === "gateway") continue;
265
272
  const h = routeHost(r);
@@ -277,7 +284,14 @@ export function routeTransport(r: TranscriptionRouteSpec, deps: TranscriptionDep
277
284
  const f = deps.fetch ?? (deps.hostPlane ?? sharedHostPlaneFetch)(roleHostPlaneHosts({ primary: r })) ?? lateFetch;
278
285
  if (r.via === "deepinfra") {
279
286
  const call = deepinfraCall(f, deps.secret ?? storeOnlySecret);
280
- return (req) => call(r, req);
287
+ const policy = deps.dataPolicy ?? (() => getSettings().models.dataPolicy);
288
+ return async (req) => {
289
+ const p = policy();
290
+ if (dataPolicyActive(p)) {
291
+ throw new Error(`models.dataPolicy (${dataPolicyRows(p).join(", ")}): the deepinfra route cannot carry these rows, so it refuses`);
292
+ }
293
+ return await call(r, req);
294
+ };
281
295
  }
282
296
  const call = selfHostedCall(f);
283
297
  return (req) => call(r, req);
@@ -2,7 +2,7 @@
2
2
  // DUCK-TYPED across processes. SECURITY: no error here carries a token, a code, or a PKCE verifier.
3
3
 
4
4
  /** The subject an error is about, as a log-safe label (never an opaque token). */
5
- export function subjectLabel(subject: { readonly type: "user"; readonly id: string } | { readonly type: "app" }): string {
5
+ function subjectLabel(subject: { readonly type: "user"; readonly id: string } | { readonly type: "app" }): string {
6
6
  return subject.type === "app" ? "app" : `user:${subject.id}`;
7
7
  }
8
8
 
@@ -601,20 +601,57 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
601
601
  });
602
602
  }
603
603
 
604
- /** Purge a person's IdP grant: revoke the IdP refresh token at the IdP (best-effort), then delete the row. */
605
- async function forgetIdpGrant(cfg: OeOauthXaaProviderConfig, subject: OeOauthSubject): Promise<boolean> {
604
+ /** How a failed IdP revocation is reported (OPE-2565): `idp:` and the issuer's host, the name an operator knows it by. */
605
+ function idpReportRef(cfg: OeOauthXaaProviderConfig): string {
606
+ try {
607
+ return `idp:${new URL(cfg.idpIssuer).host}`;
608
+ } catch {
609
+ return "idp:";
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Run one revocation call, and retry it ONCE on a failure (OPE-2565). A second failure is warned and handed to
615
+ * `onUnrevoked`, so the purge's caller records it: the row is deleted anyway, and the token stays valid at the
616
+ * provider until it expires. Never throws — a vendor failure never blocks the local delete.
617
+ */
618
+ async function revokeWithRetry(providerRef: string, call: () => Promise<void>, onUnrevoked: ((providerRef: string) => void) | undefined, what: string): Promise<void> {
619
+ for (let attempt = 1; ; attempt++) {
620
+ try {
621
+ await call();
622
+ return;
623
+ } catch (e) {
624
+ if (attempt < 2) continue;
625
+ // Message only: the client's errors are built never to carry token material.
626
+ console.warn(`[oe-oauth] ${what} failed twice for "${providerRef}" (purging locally anyway; the token stays valid there until it expires): ${e instanceof Error ? e.message : String(e)}`);
627
+ onUnrevoked?.(providerRef);
628
+ return;
629
+ }
630
+ }
631
+ }
632
+
633
+ /**
634
+ * Purge a person's IdP grant: revoke the IdP refresh token at the IdP (one retry), then delete the row. A failure
635
+ * is reported as `idp:<issuer host>`, never under the connection's ref: the token that stays valid is the IdP
636
+ * grant, which every `xaa` connection on that IdP shares, so naming the connection would send the operator to
637
+ * the wrong system.
638
+ */
639
+ async function forgetIdpGrant(cfg: OeOauthXaaProviderConfig, subject: OeOauthSubject, onUnrevoked?: (providerRef: string) => void): Promise<boolean> {
606
640
  const gRef = idpRef(cfg, subject);
607
641
  return vault.withLock(gRef, async () => {
608
642
  const grant = await readJson<IdpGrantRecord>(gRef);
609
643
  if (grant) {
610
- try {
611
- const oidc = await oidcFor(cfg);
612
- if (oidc.revocationEndpoint) {
613
- await revokeTokenConfidential(oidc.revocationEndpoint, { token: grant.refreshToken, tokenTypeHint: "refresh_token" }, await xaaClient(cfg, "idp"), doFetch);
614
- }
615
- } catch (e) {
616
- console.warn(`[oe-oauth] IdP revocation failed for "${cfg.ref}" (purging locally anyway): ${e instanceof Error ? e.message : String(e)}`);
617
- }
644
+ await revokeWithRetry(
645
+ idpReportRef(cfg),
646
+ async () => {
647
+ const oidc = await oidcFor(cfg);
648
+ if (oidc.revocationEndpoint) {
649
+ await revokeTokenConfidential(oidc.revocationEndpoint, { token: grant.refreshToken, tokenTypeHint: "refresh_token" }, await xaaClient(cfg, "idp"), doFetch);
650
+ }
651
+ },
652
+ onUnrevoked,
653
+ "IdP revocation",
654
+ );
618
655
  }
619
656
  return vault.delete(gRef);
620
657
  });
@@ -669,8 +706,9 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
669
706
  /**
670
707
  * PURGE one (provider, subject) record: vendor revocation FIRST, then the at-rest row. The ORDER makes a
671
708
  * racing cross-process refresh benign; where a vendor cannot cascade, the access token lives out its TTL.
709
+ * A revocation call that fails is retried once; a second failure goes to `onUnrevoked` (OPE-2565).
672
710
  */
673
- async function forget(providerRef: string, subject: OeOauthSubject): Promise<boolean> {
711
+ async function forget(providerRef: string, subject: OeOauthSubject, onUnrevoked?: (providerRef: string) => void): Promise<boolean> {
674
712
  const ref = tokRef(providerRef, subject);
675
713
  return vault.withLock(ref, async () => {
676
714
  const rec = await readJson<TokenRecord>(ref);
@@ -685,19 +723,23 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
685
723
  // whereas revoking an access token kills only that one.
686
724
  const token = rec.refreshToken ?? rec.accessToken;
687
725
  if (client?.revocationEndpoint && token) {
688
- try {
689
- const hint = rec.refreshToken ? ("refresh_token" as const) : ("access_token" as const);
690
- if (cfg && isStaticConfidential(cfg)) {
691
- await revokeTokenConfidential(client.revocationEndpoint, { token, tokenTypeHint: hint }, await confidentialClientFor(cfg), doFetch);
692
- } else {
693
- await revokeToken(client.revocationEndpoint, { token, clientId: client.clientId, tokenTypeHint: hint }, doFetch);
694
- }
695
- } catch (e) {
696
- // A vendor failure NEVER blocks the local delete — leaving a secret at rest with us
697
- // because someone else's server is down is the worse of the two outcomes. Message only:
698
- // the client's errors are built never to carry token material.
699
- console.warn(`[oe-oauth] revocation at the provider failed for "${providerRef}" (purging locally anyway): ${e instanceof Error ? e.message : String(e)}`);
700
- }
726
+ const endpoint = client.revocationEndpoint;
727
+ const hint = rec.refreshToken ? ("refresh_token" as const) : ("access_token" as const);
728
+ // A vendor failure NEVER blocks the local delete — leaving a secret at rest with us because someone
729
+ // else's server is down is the worse of the two outcomes. It is no longer only a warning (OPE-2565):
730
+ // after one retry it is reported, and the purge's caller records it as a failure.
731
+ await revokeWithRetry(
732
+ providerRef,
733
+ async () => {
734
+ if (cfg && isStaticConfidential(cfg)) {
735
+ await revokeTokenConfidential(endpoint, { token, tokenTypeHint: hint }, await confidentialClientFor(cfg), doFetch);
736
+ } else {
737
+ await revokeToken(endpoint, { token, clientId: client.clientId, tokenTypeHint: hint }, doFetch);
738
+ }
739
+ },
740
+ onUnrevoked,
741
+ "revocation at the provider",
742
+ );
701
743
  }
702
744
  }
703
745
  // Runs even when there was no record to read (a corrupt/undecryptable envelope reads as null —
@@ -752,22 +794,50 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
752
794
  sweepExpiredPending,
753
795
 
754
796
  /**
755
- * Purge EVERY configured provider's token record for `subject` — only the vault knows the full set. A
797
+ * Purge EVERY provider's token record for `subject` — the declared ones, and any undeclared one the vault
798
+ * still holds a row for (OPE-2560) — only the vault knows the full set. A
756
799
  * provider whose purge THROWS does not stop the others, and it rethrows only if every one failed.
757
800
  */
758
- async forgetAll(subject: OeOauthSubject): Promise<number> {
801
+ async forgetAll(subject: OeOauthSubject, onUnrevoked?: (providerRef: string) => void): Promise<number> {
759
802
  let purged = 0;
760
803
  let failures = 0;
761
804
  let lastError: unknown;
762
805
  const all = providersNow();
763
806
  for (const providerRef of all.keys()) {
764
807
  try {
765
- if (await forget(providerRef, subject)) purged++;
808
+ if (await forget(providerRef, subject, onUnrevoked)) purged++;
766
809
  } catch (e) {
767
810
  failures++;
768
811
  lastError = e;
769
812
  }
770
813
  }
814
+ // (OPE-2560) A token row for a connection that is NOT declared here is still a live grant at rest: a
815
+ // declaration an operator removed, or a process that declares none at all (the `oe erase` operator CLI
816
+ // loads no connection module). Its ref names the provider, so it goes through the same `forget`: revoked at
817
+ // the provider when the stored DCR client names a revocation endpoint, then deleted. A static provider's
818
+ // client lives only in its declaration, so with none its token is deleted locally and cannot be revoked.
819
+ // The ref is `oe-oauth:tok:<providerRef>:<subject key>`; a provider ref holds no ':', so the split is exact.
820
+ const tokPrefix = "oe-oauth:tok:";
821
+ const tokSuffix = `:${subjectKey(subject)}`;
822
+ let undeclaredAttempts = 0;
823
+ try {
824
+ for (const ref of await vault.listRefs(tokPrefix)) {
825
+ if (!ref.endsWith(tokSuffix)) continue;
826
+ const providerRef = ref.slice(tokPrefix.length, ref.length - tokSuffix.length);
827
+ if (providerRef.length === 0 || providerRef.includes(":") || all.has(providerRef)) continue;
828
+ undeclaredAttempts++;
829
+ try {
830
+ if (await forget(providerRef, subject, onUnrevoked)) purged++;
831
+ } catch (e) {
832
+ failures++;
833
+ lastError = e;
834
+ }
835
+ }
836
+ } catch (e) {
837
+ failures++;
838
+ lastError = e;
839
+ undeclaredAttempts++;
840
+ }
771
841
  // (OPE-2453) A person's IdP grant is shared by every `xaa` connection naming one IdP client, so it is purged
772
842
  // once per IdP client, after the per-connection rows. It is the credential that mints all of them.
773
843
  const seenIdp = new Set<string>();
@@ -775,7 +845,7 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
775
845
  if (!isXaa(cfg) || seenIdp.has(idpKey(cfg))) continue;
776
846
  seenIdp.add(idpKey(cfg));
777
847
  try {
778
- if (await forgetIdpGrant(cfg, subject)) purged++;
848
+ if (await forgetIdpGrant(cfg, subject, onUnrevoked)) purged++;
779
849
  } catch (e) {
780
850
  failures++;
781
851
  lastError = e;
@@ -800,7 +870,7 @@ export function makeOeOauthTokenVault(source: OeOauthProviderSource, deps: OeOau
800
870
  }
801
871
  // "Every purge attempt failed" counts only the attempts that ran: the orphan sweep is one attempt only when
802
872
  // it failed. Counting it always would let a purge where every provider failed return 0 with no error.
803
- if (failures > 0 && purged === 0 && failures === all.size + seenIdp.size + (orphanFailed ? 1 : 0)) throw lastError;
873
+ if (failures > 0 && purged === 0 && failures === all.size + undeclaredAttempts + seenIdp.size + (orphanFailed ? 1 : 0)) throw lastError;
804
874
  return purged;
805
875
  },
806
876
 
@@ -2,7 +2,7 @@
2
2
  // vault. The callback completer shares ONE vault instance, so the single-flight lock covers both paths.
3
3
 
4
4
  import type { TokenResult } from "eve/connections";
5
- import type { UserAuthFlowProvider, UserAuthSubject, UserTokenMintParams, OeOauthCallbackCompleter, CredentialPurgeHook } from "@open-everywhere/core/agent/adapter-api.js";
5
+ import type { UserAuthFlowProvider, UserAuthSubject, UserTokenMintParams, OeOauthCallbackCompleter, CredentialPurgeHook, CredentialPurgeReport } from "@open-everywhere/core/agent/adapter-api.js";
6
6
  import { makeOeOauthTokenVault, type OeOauthProviderSource, type OeOauthVaultDeps, type OeOauthSubject } from "./token-vault.js";
7
7
  import { isUserAuthRequiredError } from "./errors.js";
8
8
 
@@ -66,8 +66,9 @@ export function makeOeOauth(
66
66
  // The credential-purge hook, sharing the mint methods' vault: whatever this adapter can MINT for a subject it
67
67
  // can also DESTROY. There is no app-subject purge — the service identity is not a person.
68
68
  const purge: CredentialPurgeHook = {
69
- async purgeSubject(subjectId: string): Promise<number> {
70
- return vault.forgetAll({ type: "user", id: subjectId });
69
+ async purgeSubject(subjectId: string, report?: CredentialPurgeReport): Promise<number> {
70
+ // OPE-2565: a revocation the provider refused twice is reported, so the purge's caller records it as a failure.
71
+ return vault.forgetAll({ type: "user", id: subjectId }, report ? (providerRef) => report.unrevoked(providerRef) : undefined);
71
72
  },
72
73
  };
73
74
 
@@ -0,0 +1,47 @@
1
+ // THE HOLDERS AN OPERATOR PROCESS NEEDS TO END A PERSON'S AUTHORITY (OPE-2560) — the credential-purge
2
+ // hooks and the long-lived box backend, registered for `oe erase` outside the deployed agent.
3
+ //
4
+ // WHY THIS IS NOT `registerDefaultAdapters()`. That call is the app's boot: it declares the provider
5
+ // selection, runs every provider bundle's register(), reads the app's memory config and reconciles
6
+ // slash commands. An operator process has none of the app's modules, and it needs none of that. It needs
7
+ // exactly the two seams `revokeLinkSet` and `forgetBrowser` reach: the holders of per-person credentials,
8
+ // and the backend that holds the person's saved browser profile.
9
+ //
10
+ // THE PURGE HOOKS ARE THE BOOT'S OWN. "oe-oauth" is the same hook `registerOeOauth()` registers. With no
11
+ // connection module loaded it declares no provider, so its undeclared-token sweep (token-vault.ts
12
+ // `forgetAll`) finds the person's rows by ref. "aws-sso" registers ALWAYS here, unlike at boot (where the
13
+ // `aws.enabled` setting gates it): this process cannot read that setting, and a purge of a row that is not
14
+ // there deletes nothing and returns 0.
15
+ //
16
+ // 🔴 THE BOX BACKEND IS NAMED, NEVER GUESSED. `selectLongLivedBoxBackend()` reads `onVercel()` and the
17
+ // app's `sandbox` settings, and an operator's laptop has neither: it would pick docker for a Vercel
18
+ // deployment, and a docker forget of a missing volume succeeds. The browser record would then be deleted
19
+ // while the profile, with the person's logins, stayed on Vercel. So the operator names the backend
20
+ // (`oe erase --box-backend vercel|docker`), and with none named no backend registers: core's
21
+ // `forgetBrowser` then refuses and keeps the record.
22
+
23
+ import { registerCredentialPurgeHook, registerLongLivedBoxBackend } from "@open-everywhere/core/agent/adapter-api.js";
24
+ import { makeOeOauth } from "./oe-oauth/user-auth-flow.js";
25
+ import { loadOeOauthConfig } from "./oe-oauth/config.js";
26
+ import { makeAwsSsoPurgeHook } from "./aws/sso.js";
27
+ import { createDockerBoxBackend, createVercelBoxBackend } from "./sandbox/long-lived-box.js";
28
+
29
+ /** The box backends an operator can name: the two `selectLongLivedBoxBackend()` picks that can run a box (not justbash). */
30
+ export const OPERATOR_BOX_BACKENDS = ["vercel", "docker"] as const;
31
+ export type OperatorBoxBackend = (typeof OPERATOR_BOX_BACKENDS)[number];
32
+
33
+ /** What {@link registerOperatorHolders} registered, for the CLI to print. */
34
+ export interface OperatorHolders {
35
+ readonly purgeHooks: readonly string[];
36
+ readonly boxBackend: OperatorBoxBackend | undefined;
37
+ }
38
+
39
+ /** Register the purge hooks, and the named box backend when one is named. Idempotent overwrite. */
40
+ export function registerOperatorHolders(opts: { readonly boxBackend?: OperatorBoxBackend } = {}): OperatorHolders {
41
+ const { purge } = makeOeOauth(() => loadOeOauthConfig() ?? new Map());
42
+ registerCredentialPurgeHook("oe-oauth", purge);
43
+ registerCredentialPurgeHook("aws-sso", makeAwsSsoPurgeHook());
44
+ if (opts.boxBackend === "vercel") registerLongLivedBoxBackend(createVercelBoxBackend());
45
+ else if (opts.boxBackend === "docker") registerLongLivedBoxBackend(createDockerBoxBackend());
46
+ return { purgeHooks: ["oe-oauth", "aws-sso"], boxBackend: opts.boxBackend };
47
+ }
@@ -85,7 +85,7 @@ interface NeonConfig {
85
85
  * Point the driver at a local `wsproxy` so it reaches a plain Postgres over the compose network. NO-OP unless
86
86
  * OE_NEON_WS_PROXY is set. Insecure WS is correct for a same-network local proxy ONLY, never a remote one.
87
87
  */
88
- export function configureLocalWsProxy(neonConfig: NeonConfig): void {
88
+ function configureLocalWsProxy(neonConfig: NeonConfig): void {
89
89
  const proxy = process.env.OE_NEON_WS_PROXY?.trim();
90
90
  if (!proxy) return;
91
91
  neonConfig.wsProxy = (host, port) => `${proxy}/v1?address=${host}:${port}`;
@@ -1,7 +1,6 @@
1
- import { checkAwsSsoIdentityBinding, OE_AWS_SSO_CALLBACK_PATH, type AwsCredentials, type AwsIdentityRequest, type AwsSsoLinkStore, type CredentialPurgeHook, type EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
1
+ import { checkAwsSsoIdentityBinding, type AwsCredentials, type AwsIdentityRequest, type AwsSsoLinkStore, type CredentialPurgeHook, type EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
2
2
  /** A `fetch`-shaped transport — the DI seam the offline test injects (defaults to global fetch). */
3
3
  export type FetchLike = typeof fetch;
4
- export { OE_AWS_SSO_CALLBACK_PATH };
5
4
  /** The acting principal has not linked their Identity Center identity (or their token is dead and must
6
5
  * be re-linked). Carries the authorization URL so the caller can relay a "connect your AWS account"
7
6
  * link. Stable `code` so it is duck-typed (cross-module) as authorization-required, mirroring the
@@ -1 +1 @@
1
- {"version":3,"file":"sso.d.ts","sourceRoot":"","sources":["../../../agent/aws/sso.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sso.d.ts","sourceRoot":"","sources":["../../../agent/aws/sso.ts"],"names":[],"mappings":"AAGA,OAAO,EAIL,0BAA0B,EAE1B,KAAK,cAAc,EACnB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACtB,MAAM,4CAA4C,CAAC;AAGpD,oGAAoG;AACpG,MAAM,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;AA6CrC;;;uEAGuE;AACvE,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,YAAY,gBAAgB,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAWhD;CACF;AAED;uFACuF;AACvF,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAIxD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,gBAAqC,GAAG,eAAe,CAYjG;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,gBAAqC,GAAG,mBAAmB,CAMrG;AAED;sEACsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC5C,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,yGAAyG;AACzG,MAAM,WAAW,mBAAmB;IAClC,gGAAgG;IAChG,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6GAA6G;IAC7G,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;6GACyG;IACzG,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,kEAAkE;IAClE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;IAC3B,+FAA+F;IAC/F,QAAQ,CAAC,KAAK,CAAC,EAAE,eAAe,CAAC;IACjC,wFAAwF;IACxF,QAAQ,CAAC,OAAO,CAAC,EAAE,kBAAkB,CAAC;IACtC;;;;2GAIuG;IACvG,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,0BAA0B,CAAC;IAC1D,8FAA8F;IAC9F,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAChD,iCAAiC;IACjC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,2CAA2C;IAC3C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM;QAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACnF,+CAA+C;IAC/C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,MAAM,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,sGAAsG;AACtG,MAAM,WAAW,YAAY;IAC3B;;wFAEoF;IACpF,IAAI,CAAC,GAAG,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACvD;;6BAEyB;IACzB,SAAS,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC9D;;mGAE+F;IAC/F,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAClF;AAmBD;yGACyG;AACzG,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CASrF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAa9E;AAED,wBAAgB,gBAAgB,CAAC,IAAI,GAAE,mBAAwB,GAAG,YAAY,CAoU7E"}