@open-everywhere/adapters 0.1.5 โ 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/NOTICE +9 -9
- package/README.md +9 -1
- package/agent/browser/sandbox.ts +27 -5
- package/agent/instrumentation.ts +95 -36
- package/agent/model/approval-judge.ts +1 -1
- package/agent/oe-oauth/config.ts +1 -1
- package/agent/oe-oauth/token-vault.ts +3 -2
- package/agent/register-defaults.ts +269 -0
- package/agent/sandbox/vercel-lifecycle.ts +22 -1
- package/dist/agent/browser/sandbox.d.ts +8 -2
- package/dist/agent/browser/sandbox.d.ts.map +1 -1
- package/dist/agent/browser/sandbox.js +19 -2
- package/dist/agent/browser/sandbox.js.map +1 -1
- package/dist/agent/instrumentation.d.ts +24 -4
- package/dist/agent/instrumentation.d.ts.map +1 -1
- package/dist/agent/instrumentation.js +83 -34
- package/dist/agent/instrumentation.js.map +1 -1
- package/dist/agent/model/approval-judge.d.ts +1 -1
- package/dist/agent/model/approval-judge.js +1 -1
- package/dist/agent/oe-oauth/config.d.ts +1 -1
- package/dist/agent/oe-oauth/config.d.ts.map +1 -1
- package/dist/agent/oe-oauth/token-vault.d.ts.map +1 -1
- package/dist/agent/oe-oauth/token-vault.js +4 -2
- package/dist/agent/oe-oauth/token-vault.js.map +1 -1
- package/dist/agent/register-defaults.d.ts +76 -0
- package/dist/agent/register-defaults.d.ts.map +1 -0
- package/dist/agent/register-defaults.js +182 -0
- package/dist/agent/register-defaults.js.map +1 -0
- package/dist/agent/sandbox/vercel-lifecycle.d.ts.map +1 -1
- package/dist/agent/sandbox/vercel-lifecycle.js +20 -1
- package/dist/agent/sandbox/vercel-lifecycle.js.map +1 -1
- package/package.json +10 -5
package/NOTICE
CHANGED
|
@@ -12,28 +12,28 @@ Apache License, Version 2.0.
|
|
|
12
12
|
|
|
13
13
|
Modifications to eve
|
|
14
14
|
|
|
15
|
-
The @open-everywhere/core package distributes patches/eve+0.
|
|
16
|
-
patch file is a modification of eve 0.
|
|
17
|
-
Apache License, Version 2.0. The copyright notice of eve 0.
|
|
15
|
+
The @open-everywhere/core package distributes patches/eve+0.71.0.patch. That
|
|
16
|
+
patch file is a modification of eve 0.71.0. Vercel licenses eve 0.71.0 under the
|
|
17
|
+
Apache License, Version 2.0. The copyright notice of eve 0.71.0 reads
|
|
18
18
|
"Copyright 2026 Vercel, Inc. and contributors". The Open Everywhere authors made
|
|
19
19
|
the changes in the patch file.
|
|
20
20
|
|
|
21
|
-
The patch modifies
|
|
21
|
+
The patch modifies four files of eve 0.71.0:
|
|
22
22
|
|
|
23
|
-
dist/src/context/dynamic-tool-lifecycle.js
|
|
24
23
|
dist/src/execution/web-fetch/request.js
|
|
25
24
|
dist/src/harness/tool-loop.js
|
|
26
25
|
dist/src/internal/authored-package-boundary.js
|
|
27
26
|
dist/src/protocol/message.d.ts
|
|
28
|
-
dist/src/tasks/session-index.js
|
|
29
27
|
|
|
30
28
|
Open Everywhere does not ship the eve package. eve is a peer dependency, so the
|
|
31
|
-
consumer installs eve 0.
|
|
29
|
+
consumer installs eve 0.71.0 from the registry. The patch file carries the
|
|
32
30
|
modified portions of eve. The oe-eve-patch command, which
|
|
33
31
|
@open-everywhere/core ships, applies them to the eve that the consumer
|
|
34
|
-
installed. The command
|
|
32
|
+
installed. The command patches no file outside the consumer's own project. To
|
|
33
|
+
restore an eve that holds an older Open Everywhere patch, it also writes to npm's
|
|
34
|
+
cache and to a temporary directory.
|
|
35
35
|
|
|
36
|
-
The attribution notice of eve 0.
|
|
36
|
+
The attribution notice of eve 0.71.0 follows, as section 4(d) of the Apache
|
|
37
37
|
License, Version 2.0 requires. The notices for eve's bundled code-mode runtime
|
|
38
38
|
are not reproduced, because the patch modifies no part of that runtime.
|
|
39
39
|
|
package/README.md
CHANGED
|
@@ -17,10 +17,18 @@ It implements core's host seams against concrete services:
|
|
|
17
17
|
Core defines these seams and ships no host of its own, so a different host is a different adapters
|
|
18
18
|
package rather than a fork of the runtime.
|
|
19
19
|
|
|
20
|
+
## Boot wiring
|
|
21
|
+
|
|
22
|
+
`registerDefaultAdapters()` (`@open-everywhere/adapters/agent/register-defaults.js`) wires every
|
|
23
|
+
default adapter, the app's selected provider bundles and the operational store, with the store
|
|
24
|
+
selection rules and the floors. An app's `agent/lib/register-adapters.ts` calls it once and adds
|
|
25
|
+
its own adapters in the `customAdapters` hook. See
|
|
26
|
+
[the app's adapter wiring](https://github.com/open-everywhere/open-everywhere/blob/main/docs/extending/adapter-model.md#the-apps-adapter-wiring).
|
|
27
|
+
|
|
20
28
|
## Install
|
|
21
29
|
|
|
22
30
|
```sh
|
|
23
|
-
npm i @open-everywhere/core @open-everywhere/adapters eve@0.
|
|
31
|
+
npm i @open-everywhere/core @open-everywhere/adapters eve@0.71.0
|
|
24
32
|
npx oe-eve-patch
|
|
25
33
|
```
|
|
26
34
|
|
package/agent/browser/sandbox.ts
CHANGED
|
@@ -1,16 +1,32 @@
|
|
|
1
1
|
// The agent-browser sandbox pre-bake โ Vercel-only, and mandatory: a session sits behind the deny-all egress
|
|
2
|
-
// firewall, so no mid-turn install can work
|
|
2
|
+
// firewall, so no mid-turn install can work, so the install has to happen at BUILD.
|
|
3
|
+
//
|
|
4
|
+
// ๐ด eve 0.69.0 REMOVED `revalidationKey` (OPE-2247), so the key below reaches nothing. The sandbox
|
|
5
|
+
// definition used to pass it to eve, which froze it into the template identity; 0.69.0 keys a template
|
|
6
|
+
// by `sha256(stableSerialize(environment options))` instead, and the app now passes the bake as the
|
|
7
|
+
// environment's `prepare` (`agent/sandbox/sandbox.ts`). `stableSerialize` renders a function as its
|
|
8
|
+
// SOURCE TEXT, so arming or disarming the bake re-keys the template and editing the prepare body
|
|
9
|
+
// re-keys it, but a change to the agent-browser PIN alone does not โ the pin lives inside the vendor
|
|
10
|
+
// installer, behind a closure the hash cannot see. The key is kept, unused, because it is still the
|
|
11
|
+
// honest answer to "what would re-key this bake" and the re-derivation needs it; see the ๐ด note in
|
|
12
|
+
// `agent/sandbox/sandbox.ts`, which carries the whole account and the follow-up.
|
|
3
13
|
|
|
4
|
-
import { agentBrowserRevalidationKey, installAgentBrowser } from "@agent-browser/eve/sandbox";
|
|
14
|
+
import { agentBrowserRevalidationKey, installAgentBrowser, type EveSandboxSession } from "@agent-browser/eve/sandbox";
|
|
5
15
|
import { agentBrowserEnabled, loadAgentBrowserConfig } from "./config.js";
|
|
6
16
|
|
|
7
|
-
/** Minimal structural sandbox the installer needs
|
|
17
|
+
/** Minimal structural sandbox the installer needs. `id` is OPTIONAL deliberately: eve 0.69.0's
|
|
18
|
+
* `prepare` hands over a `SandboxSession`, which carries no `id` at all, and `installAgentBrowser`
|
|
19
|
+
* reads only `run` โ `sandbox.id` appears once in the vendor bundle, inside `runAgentBrowser` (the
|
|
20
|
+
* TOOL path). Keeping it required would make the caller's cast load-bearing and hide the day a real
|
|
21
|
+
* `id` dependency comes back. @agent-browser/eve's own `EveSandboxSession` still requires it, so the
|
|
22
|
+
* one cast at the `installAgentBrowser` boundary stays and is commented there. */
|
|
8
23
|
export interface BakeableSandbox {
|
|
9
|
-
readonly id
|
|
24
|
+
readonly id?: string;
|
|
10
25
|
run(options: { readonly command: string }): PromiseLike<{ readonly exitCode?: number; readonly stderr?: string; readonly stdout?: string }>;
|
|
11
26
|
}
|
|
12
27
|
|
|
13
28
|
export interface AgentBrowserSandboxHooks {
|
|
29
|
+
/** ๐ด DEAD SINCE eve 0.69.0 (OPE-2247) โ nothing reads it. See this file's header. */
|
|
14
30
|
readonly revalidationKey: () => string;
|
|
15
31
|
readonly bootstrap: (input: { use: () => Promise<BakeableSandbox> }) => Promise<void>;
|
|
16
32
|
}
|
|
@@ -20,7 +36,13 @@ export interface AgentBrowserSandboxHooks {
|
|
|
20
36
|
* vendor installer: system deps โ `npm install -g agent-browser@<pinned>` โ `agent-browser install`).
|
|
21
37
|
*/
|
|
22
38
|
export function agentBrowserSandboxHooks(
|
|
23
|
-
|
|
39
|
+
// ๐ด THE ONE CAST, AND IT IS HERE ON PURPOSE. The vendor's `EveSandboxSession` requires an `id`;
|
|
40
|
+
// `installAgentBrowser` never reads one (verified in the vendor bytes: `sandbox.id` appears once, in
|
|
41
|
+
// `runAgentBrowser`, which is the TOOL path), and eve 0.69.0's `prepare` has no `id` to give. So the
|
|
42
|
+
// widening sits at THIS boundary, where the mismatch actually is, rather than at the caller โ which
|
|
43
|
+
// is what makes `BakeableSandbox.id` optional honest instead of a hole: the day the installer does
|
|
44
|
+
// read an `id`, this line is the one that has to be argued with.
|
|
45
|
+
install: (sandbox: BakeableSandbox) => Promise<unknown> = (sandbox) => installAgentBrowser(sandbox as EveSandboxSession),
|
|
24
46
|
): AgentBrowserSandboxHooks | Record<string, never> {
|
|
25
47
|
if (!agentBrowserEnabled()) return {};
|
|
26
48
|
// ๐ด ONE ROAD, NOT TWO (OPE-1793). The platform refusal lives in the config loader, which every
|
package/agent/instrumentation.ts
CHANGED
|
@@ -1,42 +1,101 @@
|
|
|
1
|
-
// OpenTelemetry
|
|
2
|
-
//
|
|
3
|
-
// eve
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
// OpenTelemetry through eve's own pipeline; the vendor SDK (`@vercel/otel` exporters) is why this lives in adapters.
|
|
2
|
+
//
|
|
3
|
+
// eve 0.71 owns the OpenTelemetry lifecycle: it registers ONE tracer provider for the process, from
|
|
4
|
+
// the providers in `agent/instrumentation/`. On Vercel preview and production it also seeds its Agent
|
|
5
|
+
// Runs destination. A second `registerOTel` call kills the boot ("another runtime already owns the
|
|
6
|
+
// global tracer provider", OPE-2262), so this module never registers anything itself. It declares:
|
|
7
|
+
//
|
|
8
|
+
// - `default` (the app's `otel` slot): the process-wide trace policy, plus the boot `setup`.
|
|
9
|
+
// The policy emits every trace with NO model input and NO model output, for every audience in
|
|
10
|
+
// every environment. Without it, eve records content for public conversations and in
|
|
11
|
+
// development. A destination cannot restore what this ceiling removes.
|
|
12
|
+
// - `otlpDestination` (the app's `otlp` slot): the consumer's OTLP collector, from the standard
|
|
13
|
+
// `OTEL_EXPORTER_OTLP_*` variables, as an eve destination. With no endpoint it is disabled.
|
|
14
|
+
//
|
|
15
|
+
// `scripts/instrumentation-hosted-boot-test.ts` boots both slots through eve's registration path
|
|
16
|
+
// with the env Vercel creates.
|
|
17
|
+
|
|
18
|
+
import { disableInstrumentation } from "eve/instrumentation";
|
|
19
|
+
import { otel, otelIntegration, type OtelDeclaration, type SpanExporter } from "eve/instrumentation/otel";
|
|
20
|
+
import { OTLPHttpJsonTraceExporter, OTLPHttpProtoTraceExporter } from "@vercel/otel";
|
|
12
21
|
import { installLogShipping, initTelemetry } from "@open-everywhere/core/agent/adapter-api.js";
|
|
13
22
|
|
|
23
|
+
/** Metadata only: emit the span, record neither content direction. */
|
|
24
|
+
const METADATA_ONLY = { emit: true, recordInputs: false, recordOutputs: false } as const;
|
|
25
|
+
|
|
14
26
|
/**
|
|
15
|
-
*
|
|
16
|
-
* `
|
|
27
|
+
* Boot side effects. eve awaits every provider's `setup` before it loads the server entry.
|
|
28
|
+
* `OtelDeclaration` is an eve `InstrumentationProvider`, so it carries `setup` like any other.
|
|
17
29
|
*/
|
|
18
|
-
function
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
30
|
+
function setup(): void {
|
|
31
|
+
// Structured log shipping (vendor-neutral HTTP ingest; core seam). Its own env gate
|
|
32
|
+
// (OE_LOG_SHIP_URL); console is untouched when that is unset.
|
|
33
|
+
installLogShipping();
|
|
34
|
+
// Anonymous usage telemetry (default ON; core seam, own env gate โ OE_TELEMETRY/DO_NOT_TRACK).
|
|
35
|
+
// Prints the boot disclosure line before anything is transmitted. Fire-and-forget: boot never waits.
|
|
36
|
+
void initTelemetry();
|
|
24
37
|
}
|
|
25
38
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
const declaration: OtelDeclaration = { ...otel({ tracePolicy: () => METADATA_ONLY }), setup };
|
|
40
|
+
export default declaration;
|
|
41
|
+
|
|
42
|
+
/** The OTLP traces protocols `@vercel/otel` exports over HTTP. */
|
|
43
|
+
type OtlpProtocol = "http/protobuf" | "http/json";
|
|
44
|
+
|
|
45
|
+
/** The OTLP target the standard env vars name, or `undefined` when no endpoint is set. */
|
|
46
|
+
export interface OtlpTarget {
|
|
47
|
+
readonly url: string;
|
|
48
|
+
readonly protocol: OtlpProtocol;
|
|
49
|
+
readonly headers: Readonly<Record<string, string>>;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Parse an `OTEL_EXPORTER_OTLP_*HEADERS` value: comma-separated `key=value` pairs, each value
|
|
54
|
+
* percent-encoded (OpenTelemetry SDK environment specification). A pair without `=` is skipped.
|
|
55
|
+
*/
|
|
56
|
+
export function parseOtlpHeaders(raw: string | undefined): Record<string, string> {
|
|
57
|
+
const headers: Record<string, string> = {};
|
|
58
|
+
for (const pair of (raw ?? "").split(",")) {
|
|
59
|
+
const at = pair.indexOf("=");
|
|
60
|
+
if (at <= 0) continue;
|
|
61
|
+
const key = pair.slice(0, at).trim();
|
|
62
|
+
const value = pair.slice(at + 1).trim();
|
|
63
|
+
if (key === "") continue;
|
|
64
|
+
try {
|
|
65
|
+
headers[key] = decodeURIComponent(value);
|
|
66
|
+
} catch {
|
|
67
|
+
headers[key] = value;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return headers;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Resolve the OTLP traces target with the standard precedence: the traces-specific variable over the
|
|
75
|
+
* general one, and the general endpoint gets `/v1/traces` appended. Only the HTTP protocols exist
|
|
76
|
+
* here; `grpc` or an unknown value falls back to `http/protobuf` with a warning.
|
|
77
|
+
*/
|
|
78
|
+
export function resolveOtlpTarget(env: NodeJS.ProcessEnv = process.env): OtlpTarget | undefined {
|
|
79
|
+
const tracesEndpoint = env.OTEL_EXPORTER_OTLP_TRACES_ENDPOINT?.trim();
|
|
80
|
+
const endpoint = env.OTEL_EXPORTER_OTLP_ENDPOINT?.trim();
|
|
81
|
+
const url = tracesEndpoint ? tracesEndpoint : endpoint ? `${endpoint.replace(/\/+$/, "")}/v1/traces` : undefined;
|
|
82
|
+
if (url === undefined) return undefined;
|
|
83
|
+
const requested = (env.OTEL_EXPORTER_OTLP_TRACES_PROTOCOL ?? env.OTEL_EXPORTER_OTLP_PROTOCOL ?? "http/protobuf").trim();
|
|
84
|
+
let protocol: OtlpProtocol = "http/protobuf";
|
|
85
|
+
if (requested === "http/json") protocol = "http/json";
|
|
86
|
+
else if (requested !== "http/protobuf") {
|
|
87
|
+
console.warn(`[open-everywhere:instrumentation] OTLP traces protocol "${requested}" is not supported โ exporting with http/protobuf`);
|
|
88
|
+
}
|
|
89
|
+
const headers = { ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_HEADERS), ...parseOtlpHeaders(env.OTEL_EXPORTER_OTLP_TRACES_HEADERS) };
|
|
90
|
+
return { url, protocol, headers };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function otlpExporter(target: OtlpTarget): SpanExporter {
|
|
94
|
+
const config = { url: target.url, headers: { ...target.headers } };
|
|
95
|
+
return target.protocol === "http/json" ? new OTLPHttpJsonTraceExporter(config) : new OTLPHttpProtoTraceExporter(config);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const OTLP_TARGET = resolveOtlpTarget();
|
|
99
|
+
|
|
100
|
+
/** The consumer's OTLP collector as an eve destination, or disabled when no endpoint is set. */
|
|
101
|
+
export const otlpDestination = OTLP_TARGET === undefined ? disableInstrumentation() : otelIntegration({ traceExporter: otlpExporter(OTLP_TARGET) });
|
|
@@ -50,7 +50,7 @@ function gatewayApprovalJudgeCompletion(defaults: GatewayRoutedDefaults): Approv
|
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
52
|
* Build and register the gateway judge transport at boot. Without it the judge answers `defer` everywhere;
|
|
53
|
-
* registration IS the transport opt-in, and arming is separate (approvals.judgeMode,
|
|
53
|
+
* registration IS the transport opt-in, and arming is separate (approvals.judgeMode, shipped enforce).
|
|
54
54
|
*/
|
|
55
55
|
export function registerGatewayApprovalJudge(defaults: GatewayRoutedDefaults): void {
|
|
56
56
|
registerApprovalJudgeCompletion(gatewayApprovalJudgeCompletion(defaults));
|
package/agent/oe-oauth/config.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface OeOauthDiscoveryProviderConfig extends OeOauthProviderConfigBas
|
|
|
32
32
|
readonly mode: typeof OE_OAUTH_MODE_DISCOVERY;
|
|
33
33
|
/** The hosted-MCP server URL โ discovery base + RFC 8707 resource indicator. */
|
|
34
34
|
readonly resource: string;
|
|
35
|
-
/** The `client_name` sent at Dynamic Client Registration (default
|
|
35
|
+
/** The `client_name` sent at Dynamic Client Registration (default: the deployment's configured name, `agentDisplayName()`). */
|
|
36
36
|
readonly clientName?: string;
|
|
37
37
|
}
|
|
38
38
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// encrypted KV. Refresh is single-flight in-process AND cross-process, because the provider ROTATES tokens.
|
|
3
3
|
|
|
4
4
|
import type { EncryptedKvVault } from "@open-everywhere/core/agent/adapter-api.js";
|
|
5
|
-
import { encryptedKvVault, getSecretVia, OE_OAUTH_CALLBACK_PATH } from "@open-everywhere/core/agent/adapter-api.js";
|
|
5
|
+
import { agentDisplayName, encryptedKvVault, getSecretVia, OE_OAUTH_CALLBACK_PATH } from "@open-everywhere/core/agent/adapter-api.js";
|
|
6
6
|
import { isStaticConfidential, OE_OAUTH_MODE_STATIC, type OeOauthProviderConfig, type OeOauthStaticProviderConfig } from "./config.js";
|
|
7
7
|
import {
|
|
8
8
|
discoverEndpoints,
|
|
@@ -250,7 +250,8 @@ export function makeOeOauthTokenVault(providers: ReadonlyMap<string, OeOauthProv
|
|
|
250
250
|
const endpoints = await discoverEndpoints(cfg.resource, doFetch);
|
|
251
251
|
const { clientId } = await registerClient(
|
|
252
252
|
endpoints.registrationEndpoint,
|
|
253
|
-
|
|
253
|
+
// The OAuth consent screen names the client: the deployment's configured name (OPE-2334).
|
|
254
|
+
{ clientName: cfg.clientName ?? agentDisplayName(), redirectUri, ...(cfg.scopes ? { scopes: cfg.scopes } : {}) },
|
|
254
255
|
doFetch,
|
|
255
256
|
);
|
|
256
257
|
const record: DcrClientRecord = {
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
// THE DEFAULT ADAPTER WIRING (OPE-2301) โ `registerDefaultAdapters()`, the boot call every app makes
|
|
2
|
+
// from its `agent/lib/register-adapters.ts`.
|
|
3
|
+
//
|
|
4
|
+
// ๐ด WHY THIS IS PRODUCT CODE AND NOT A TEMPLATE FILE. The wiring used to live in each app, as a
|
|
5
|
+
// copy of the template's `agent/lib/register-adapters.ts`. `npx @open-everywhere/create` copies the
|
|
6
|
+
// template once, and an upgrade touches only `package.json`, so every upgrade had to hand-merge the
|
|
7
|
+
// copy. It drifted twice: a 0.1.2 copy registered no Postgres client on a bare `DATABASE_URL`
|
|
8
|
+
// (OPE-2230), and the test-bed's copy broke the 0.1.5 build with a boot refusal. The selection rules
|
|
9
|
+
// and the floors below are product logic, not consumer config. Here they ship with the package, and
|
|
10
|
+
// an upgrade picks them up. The app's file is one call plus an optional `customAdapters` hook.
|
|
11
|
+
//
|
|
12
|
+
// ๐ด STATIC IMPORTS ON PURPOSE. Eve bundles each authored module and its import graph into ONE
|
|
13
|
+
// rolldown chunk. A literal dynamic `import("โฆ")` of an internal module code-splits into a second
|
|
14
|
+
// chunk and trips Eve's "expected one authored module" invariant. Laziness is kept where it matters:
|
|
15
|
+
// `registerNeon()` builds the `@neondatabase/serverless` driver through an inner variable-specifier
|
|
16
|
+
// import, and only when it is CALLED (the Neon gate below).
|
|
17
|
+
//
|
|
18
|
+
// ๐ด THE APP'S INPUTS ARE ARGUMENTS. The selection table (`providers.generated.ts`, `oe build`
|
|
19
|
+
// output) and the memory config are app files, so the app passes them in. This module names no
|
|
20
|
+
// provider bundle: it drives the table it receives.
|
|
21
|
+
|
|
22
|
+
import { registerOeOauth } from "./oe-oauth/register.js";
|
|
23
|
+
import { oeOauthEnabled, loadOeOauthConfig } from "./oe-oauth/config.js";
|
|
24
|
+
import { registerAgentBrowser } from "./browser/register.js";
|
|
25
|
+
import { agentBrowserEnabled, loadAgentBrowserConfig } from "./browser/config.js";
|
|
26
|
+
import { registerAwsAccess } from "./aws/register.js";
|
|
27
|
+
import { awsAccessEnabled } from "./aws/config.js";
|
|
28
|
+
import { registerDirectoryAdapter } from "./directory/register.js";
|
|
29
|
+
import { registerGatewayRouteProbe } from "./model/gateway-probe.js";
|
|
30
|
+
import { registerNeon } from "./postgres/neon.js";
|
|
31
|
+
import { vercelBlobPutter } from "./blob/hosting.js";
|
|
32
|
+
import {
|
|
33
|
+
registerDbCredentialBroker,
|
|
34
|
+
CREDENTIAL_KEY_ENV,
|
|
35
|
+
registerGitHubConfigPrProvider,
|
|
36
|
+
setBlobPutter,
|
|
37
|
+
registerAuditDropCounter,
|
|
38
|
+
emitTelemetry,
|
|
39
|
+
getOperationalStore,
|
|
40
|
+
bootVerifyAuditChainTail,
|
|
41
|
+
bootBackfillLinkTenants,
|
|
42
|
+
runBootNotices,
|
|
43
|
+
registerOperationalPgliteLazy,
|
|
44
|
+
resolveOperationalSelection,
|
|
45
|
+
resolveMemoryProviderId,
|
|
46
|
+
onVercel,
|
|
47
|
+
declareProviderSelection,
|
|
48
|
+
assertConnectorMemoryGrants,
|
|
49
|
+
applySubtreeSurfaceClaims,
|
|
50
|
+
reconcileSurfaceCommands,
|
|
51
|
+
type Domain,
|
|
52
|
+
type DomainMemoryGrants,
|
|
53
|
+
type MemorySilo,
|
|
54
|
+
type OperationalFloor,
|
|
55
|
+
} from "@open-everywhere/core/agent/adapter-api.js";
|
|
56
|
+
|
|
57
|
+
/** One row of the app's generated selection table (`agent/lib/providers.generated.ts`). */
|
|
58
|
+
export interface SelectedProviderBundle {
|
|
59
|
+
readonly id: string;
|
|
60
|
+
readonly register: () => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface DefaultAdapterOptions {
|
|
64
|
+
/** The app's `SELECTED_PROVIDERS` table. Its ids are the declared selection. */
|
|
65
|
+
readonly providers: readonly SelectedProviderBundle[];
|
|
66
|
+
/**
|
|
67
|
+
* The app's memory config, for the prefetch invariant. Each one is CALLED at the check, after the
|
|
68
|
+
* provider bundles register, so pass the loaders, not their results.
|
|
69
|
+
*/
|
|
70
|
+
readonly memory: {
|
|
71
|
+
readonly domains: () => readonly Domain[];
|
|
72
|
+
readonly silos: () => readonly MemorySilo[];
|
|
73
|
+
readonly grants: (domain: Domain) => DomainMemoryGrants | null;
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* THE HOOK FOR CUSTOM ADAPTERS. It runs once, after every default adapter and every selected
|
|
77
|
+
* provider bundle registers, and before the boot checks that read those registries (the memory
|
|
78
|
+
* grants, the subtree claims), the store wiring and the slash-command reconcile. So a custom
|
|
79
|
+
* registration here replaces a default one, and the same boot checks see it.
|
|
80
|
+
* The store selection is NOT a hook point: the Neon gate and the PGlite floor are product rules.
|
|
81
|
+
*/
|
|
82
|
+
readonly customAdapters?: () => void | Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** What the store wiring decided for one env. */
|
|
86
|
+
export interface StoreWiring {
|
|
87
|
+
readonly operational: { readonly id: string; readonly floor: OperationalFloor };
|
|
88
|
+
/** Did the Neon gate open (a Postgres backing is selected AND `DATABASE_URL` is set)? */
|
|
89
|
+
readonly neon: boolean;
|
|
90
|
+
/** Is the embedded PGlite floor armed? */
|
|
91
|
+
readonly pgliteFloor: boolean;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** What one `registerDefaultAdapters()` call wired. */
|
|
95
|
+
export interface DefaultAdapterWiring {
|
|
96
|
+
readonly providers: readonly string[];
|
|
97
|
+
readonly oeOauth: boolean;
|
|
98
|
+
readonly dbCredentialBroker: boolean;
|
|
99
|
+
readonly agentBrowser: boolean;
|
|
100
|
+
readonly awsAccess: boolean;
|
|
101
|
+
readonly customAdapters: boolean;
|
|
102
|
+
readonly store: StoreWiring;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The store decision, pure. ๐ด THE SELECTION IS THE RUNTIME'S OWN RULE, NOT A SECOND ENV TEST
|
|
107
|
+
* (OPE-2230). Both selectors default to postgres when `DATABASE_URL` is present. A hand-written
|
|
108
|
+
* `OE_MEMORY_PROVIDER === "postgres"` test refuses to build the driver for exactly the deployment
|
|
109
|
+
* the default exists for: the Neon Marketplace integration sets `DATABASE_URL` and nothing else.
|
|
110
|
+
*
|
|
111
|
+
* The PGlite floor (OPE-113) arms when the operational selection resolves to it (no provider named,
|
|
112
|
+
* no `DATABASE_URL`). It stays off on Vercel, where the floor is per-instance and ephemeral, unless
|
|
113
|
+
* `OE_ALLOW_EPHEMERAL_OPERATIONAL=1` names the single-instance escape. The Neon gate never opens on
|
|
114
|
+
* the floor's branch, so the two never both register.
|
|
115
|
+
*/
|
|
116
|
+
export function decideStoreWiring(env: NodeJS.ProcessEnv, vercel: boolean): StoreWiring {
|
|
117
|
+
const operational = resolveOperationalSelection(env);
|
|
118
|
+
const postgresSelected =
|
|
119
|
+
resolveMemoryProviderId(env) === "postgres" || (operational.id === "postgres" && operational.floor === "none");
|
|
120
|
+
return {
|
|
121
|
+
operational,
|
|
122
|
+
neon: postgresSelected && Boolean(env.DATABASE_URL),
|
|
123
|
+
pgliteFloor: operational.floor === "pglite" && (!vercel || env.OE_ALLOW_EPHEMERAL_OPERATIONAL === "1"),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let WIRED: DefaultAdapterWiring | undefined;
|
|
128
|
+
let STARTED: Promise<DefaultAdapterWiring> | undefined;
|
|
129
|
+
|
|
130
|
+
/** What `registerDefaultAdapters()` wired in this process, or `undefined` before it runs. */
|
|
131
|
+
export function defaultAdapterWiring(): DefaultAdapterWiring | undefined {
|
|
132
|
+
return WIRED;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Wire every default adapter, the selected provider bundles and the operational store. Call it from
|
|
137
|
+
* the app's `agent/lib/register-adapters.ts`, with `await` at module top level.
|
|
138
|
+
* The wiring runs ONCE per process. A second run would declare the provider selection again (which
|
|
139
|
+
* clears the gate's admitted and refused records), and run every bundle's register(), the boot
|
|
140
|
+
* checks and the slash-command reconcile a second time.
|
|
141
|
+
* ๐ด A SECOND CALL RETURNS THE FIRST CALL'S RESULT; IT DOES NOT THROW. eve 0.71's authored-module
|
|
142
|
+
* loader (`internal/authored-module-loader.js`) bundles and evaluates `agent/agent.ts` more than once
|
|
143
|
+
* in one `eve build` process, and each evaluation runs `register-adapters.ts` again. This module and
|
|
144
|
+
* core stay external, so the second evaluation shares the registries the first one filled. A throw
|
|
145
|
+
* here failed that evaluation and the build (OPE-2346).
|
|
146
|
+
*/
|
|
147
|
+
export function registerDefaultAdapters(options: DefaultAdapterOptions): Promise<DefaultAdapterWiring> {
|
|
148
|
+
STARTED ??= wireDefaultAdapters(options);
|
|
149
|
+
return STARTED;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function wireDefaultAdapters(options: DefaultAdapterOptions): Promise<DefaultAdapterWiring> {
|
|
153
|
+
|
|
154
|
+
// PROVIDER SELECTION โ declared FIRST, before any bundle registers (OPE-116 stage 5). Core takes the
|
|
155
|
+
// ids as opaque strings, and every platform-keyed registration seam then refuses anything outside
|
|
156
|
+
// them. A row that lands ahead of the declaration cannot be retracted (core warns if one does).
|
|
157
|
+
const providers = declareProviderSelection(options.providers.map((p) => p.id));
|
|
158
|
+
|
|
159
|
+
// Connector-plane user-auth flow (OPE-88): own-rolled OAuth, registered only when the master flag
|
|
160
|
+
// OE_OAUTH_ENABLED is armed AND a provider config block is present. Otherwise no flow registers and
|
|
161
|
+
// core's OAuth mint paths fail closed. There is no vendor fallback.
|
|
162
|
+
const oeOauthCfg = oeOauthEnabled() ? loadOeOauthConfig() : undefined;
|
|
163
|
+
if (oeOauthCfg) registerOeOauth({ providers: oeOauthCfg });
|
|
164
|
+
|
|
165
|
+
// The "db" credential broker (task #113): registered iff its AES key exists. Without the key the
|
|
166
|
+
// broker fails closed on store and read, so registering it would falsely offer the "Set API key"
|
|
167
|
+
// chat intake, which keys off the broker being REGISTERED.
|
|
168
|
+
const dbCredentialBroker = Boolean(process.env[CREDENTIAL_KEY_ENV]);
|
|
169
|
+
if (dbCredentialBroker) registerDbCredentialBroker();
|
|
170
|
+
|
|
171
|
+
// The agent-browser toolkit (OPE-43). The platform gate is inside loadAgentBrowserConfig
|
|
172
|
+
// (OPE-1793): off Vercel the config is `undefined` and nothing registers.
|
|
173
|
+
// registerAgentBrowser can still refuse (returns false, loud) when the vendor binding did not take.
|
|
174
|
+
const browserCfg = agentBrowserEnabled() ? loadAgentBrowserConfig() : undefined;
|
|
175
|
+
const agentBrowser = browserCfg ? registerAgentBrowser(browserCfg) : false;
|
|
176
|
+
|
|
177
|
+
// The AWS-access layer: only when OE_AWS_ENABLED is armed. Flag-off โ core's AWS mint and exec
|
|
178
|
+
// paths fail closed.
|
|
179
|
+
const awsAccess = awsAccessEnabled();
|
|
180
|
+
if (awsAccess) registerAwsAccess();
|
|
181
|
+
|
|
182
|
+
// The identity-DIRECTORY plane: a no-op unless OE_DIRECTORY_PROVIDER names an adapter, and LOUD
|
|
183
|
+
// when it names one whose config is incomplete.
|
|
184
|
+
registerDirectoryAdapter();
|
|
185
|
+
|
|
186
|
+
// The GitHub config-repo PR mechanism, the default ConfigPrProvider (OPE-86). Unconditional and
|
|
187
|
+
// harmless when unused. A self-host swaps in its own provider from `customAdapters`.
|
|
188
|
+
registerGitHubConfigPrProvider();
|
|
189
|
+
|
|
190
|
+
// THE PROVIDER BUNDLES: one boot call per SELECTED bundle, straight from the generated table.
|
|
191
|
+
for (const provider of options.providers) {
|
|
192
|
+
provider.register();
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// The deployment-status surface's model probe (OPE-2230). It registers a function, and nothing
|
|
196
|
+
// calls it until somebody reads `GET /eve/v1/oe/status`.
|
|
197
|
+
registerGatewayRouteProbe();
|
|
198
|
+
|
|
199
|
+
// The audit drop counter (audit-wedge WS-5). audit.ts cannot import telemetry.ts (a module-init
|
|
200
|
+
// cycle, and the one-chunk invariant), so this boot module injects the emitter.
|
|
201
|
+
registerAuditDropCounter((action, platform) => emitTelemetry("oe.audit.dropped", { "oe.action": action, "oe.platform": platform }));
|
|
202
|
+
|
|
203
|
+
// The Vercel Blob putter for the HTML-artifact hosting seam. Harmless when unused: the hosting
|
|
204
|
+
// path runs only for an org that opted in, and the putter fails loud without its OIDC env.
|
|
205
|
+
setBlobPutter(vercelBlobPutter);
|
|
206
|
+
|
|
207
|
+
// THE APP'S HOOK, after every default registration and before every check that reads them.
|
|
208
|
+
const customAdapters = options.customAdapters !== undefined;
|
|
209
|
+
if (options.customAdapters) await options.customAdapters();
|
|
210
|
+
|
|
211
|
+
// THE PREFETCH INVARIANT (OPE-390), fail-LOUD. It runs after the bundles register, because a
|
|
212
|
+
// connector-owned memory capability is DECLARED by its bundle's register(). It refuses a domain
|
|
213
|
+
// whose memory names a connector-owned silo the domain holds no connector grant for.
|
|
214
|
+
assertConnectorMemoryGrants(options.memory.domains(), options.memory.silos(), options.memory.grants);
|
|
215
|
+
|
|
216
|
+
// THE SUBTREE CLAIM (OPE-539), for the same boot-order reason: the platform that honours
|
|
217
|
+
// `subtree: true` declares so from its own bundle's register().
|
|
218
|
+
applySubtreeSurfaceClaims();
|
|
219
|
+
|
|
220
|
+
const store = decideStoreWiring(process.env, onVercel());
|
|
221
|
+
|
|
222
|
+
// Neon (hosted Postgres) is LIVE-ONLY: registerNeon() builds the driver only when called here.
|
|
223
|
+
if (store.neon) await registerNeon();
|
|
224
|
+
|
|
225
|
+
// The embedded PGlite floor, armed LAZILY (OPE-391). The store registers now, but the lock, the
|
|
226
|
+
// WASM boot and the schema init happen on the first QUERY. `eve start` evaluates the app module
|
|
227
|
+
// in the CLI parent and again in the server child, and an eager arm made the parent the lock
|
|
228
|
+
// holder. The boot store checks ride along as the first-open callback, so they run once, in the
|
|
229
|
+
// process that uses the store.
|
|
230
|
+
if (store.pgliteFloor) registerOperationalPgliteLazy(undefined, runBootStoreChecks);
|
|
231
|
+
else await runBootStoreChecks();
|
|
232
|
+
|
|
233
|
+
// SLASH COMMANDS REGISTER FROM THE DEPLOYMENT ITSELF (OPE-1687). The app declares WHICH ROOMS
|
|
234
|
+
// (`declareSurfaceCommandScope`); each selected bundle declared HOW in the loop above. AWAITED, and
|
|
235
|
+
// it never throws: a lambda can freeze the moment it answers, and a floating promise would be
|
|
236
|
+
// cancelled on the cold start that had the work to do. The reconciler owns the latency bound (a
|
|
237
|
+
// short timeout per vendor call, no 429 backoff, one budget). `eve start` evaluates the app module
|
|
238
|
+
// twice, so a local start reconciles twice. A reconcile WRITES when the process holds the surface's
|
|
239
|
+
// credentials and the declared rooms differ from this checkout.
|
|
240
|
+
await reconcileSurfaceCommands();
|
|
241
|
+
|
|
242
|
+
WIRED = { providers, oeOauth: oeOauthCfg !== undefined, dbCredentialBroker, agentBrowser, awsAccess, customAdapters, store };
|
|
243
|
+
return WIRED;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// The boot-time store checks, in one place so they run either eagerly (hosted and sqlite paths, where
|
|
247
|
+
// the store is live the moment it registers) or on the PGlite floor's FIRST OPEN. Availability over
|
|
248
|
+
// integrity: none of them throws.
|
|
249
|
+
// 1. The audit hash-chain tail-check (OPE-52): one loud warn on any break.
|
|
250
|
+
// 2. The identity-link tenant backfill (OPE-309): fills blank `tenant` columns only.
|
|
251
|
+
// 3. The registered boot notices (OPE-2231): a selected bundle registers a notice from its own
|
|
252
|
+
// register() call, and this drains the registry. The drain catches each notice separately.
|
|
253
|
+
async function runBootStoreChecks(): Promise<void> {
|
|
254
|
+
try {
|
|
255
|
+
await bootVerifyAuditChainTail(getOperationalStore());
|
|
256
|
+
} catch {
|
|
257
|
+
/* store not resolvable โ its first real use raises the reason loudly; skip the check */
|
|
258
|
+
}
|
|
259
|
+
try {
|
|
260
|
+
await bootBackfillLinkTenants();
|
|
261
|
+
} catch {
|
|
262
|
+
/* store not resolvable โ see above; bootBackfillLinkTenants swallows everything else */
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
await runBootNotices();
|
|
266
|
+
} catch {
|
|
267
|
+
/* unreachable โ runBootNotices catches per notice; see core boot/notices.ts */
|
|
268
|
+
}
|
|
269
|
+
}
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
// Stop-after-turn sandbox lifecycle โ this file owns the DECISION, backend-stop.ts the mechanics. It exists
|
|
2
2
|
// because a session VM is billed for its whole idle tail; every failure is fail-soft to the `timeout` fuse.
|
|
3
|
+
//
|
|
4
|
+
// ๐ด ON A WORK SESSION THE RULE IS "STOP WHEN IDLE", NOT "STOP AFTER THE TURN" (OPE-2341, placement ยง2.9).
|
|
5
|
+
// In the three-tier shape the WORK SESSION (W) owns the conversation's sandbox, and every job of the
|
|
6
|
+
// conversation runs in it. A W turn can end while W still owes a job: a job forwarded and not yet
|
|
7
|
+
// started, or a task whose settlement has not run yet. The session-local live set does not see the
|
|
8
|
+
// first, so the old rule stopped the VM under a job that was about to run in it. So on W the stop also
|
|
9
|
+
// needs W's DURABLE ledger to owe nothing (core channels/work-generation.ts `workSessionSandboxState`).
|
|
10
|
+
// The rule for every other session is unchanged: a seat has no sandbox of its own, and a two-tier seat
|
|
11
|
+
// keeps the stop after its turn. `timeoutMs` stays the fuse, so on W it is the longest a JOB may hold
|
|
12
|
+
// the VM, not the longest turn.
|
|
3
13
|
|
|
4
14
|
import { defineHook, type HookContext } from "eve/hooks";
|
|
5
|
-
import { bindFrom, domainKeyOf, resolvePartition, appendUsageEvent, usageForSession, recordSpend, periodOf, estimateSandboxCostUsd, routineIdOf, turnSurfaceMode, getSettings, isSubagentChildContext, liveTaskIds, subagentParentSessionId, type BindableContext } from "@open-everywhere/core/agent/adapter-api.js";
|
|
15
|
+
import { bindFrom, domainKeyOf, resolvePartition, appendUsageEvent, usageForSession, recordSpend, periodOf, estimateSandboxCostUsd, routineIdOf, turnSurfaceMode, getSettings, isSubagentChildContext, liveTaskIds, subagentParentSessionId, workSessionSandboxState, type BindableContext } from "@open-everywhere/core/agent/adapter-api.js";
|
|
6
16
|
import { resolveSandboxStopper, shouldStop, type SandboxListing, type SandboxStopper } from "./backend-stop.js";
|
|
7
17
|
|
|
8
18
|
// Re-exported so the one "is this sandbox up?" predicate and the one listing shape stay
|
|
@@ -122,6 +132,17 @@ async function onTurnEnd(ctx: HookContext, outcome: string): Promise<void> {
|
|
|
122
132
|
);
|
|
123
133
|
return;
|
|
124
134
|
}
|
|
135
|
+
// ๐ด OPE-2341 โ A WORK SESSION STOPS ITS SANDBOX ONLY WHEN IDLE (see the file header). Fail-closed:
|
|
136
|
+
// an unreadable ledger reads as busy, and the VM then idles to its fuse instead of dying under a job.
|
|
137
|
+
const work = await workSessionSandboxState(ctx as unknown as BindableContext);
|
|
138
|
+
if (work.work && !work.idle) {
|
|
139
|
+
log(
|
|
140
|
+
`turn.${outcome}: SKIPPED the stop โ this work session is not idle` +
|
|
141
|
+
(work.owed.length > 0 ? `: it still owes ${work.owed.length} job(s) or task(s) [${work.owed.join(", ")}]` : ": its ledger could not be read") +
|
|
142
|
+
"; the turn end that finds it idle sweeps it",
|
|
143
|
+
);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
125
146
|
const stopped = await stopSessionSandboxes(ctx.session.id, stopper);
|
|
126
147
|
if (stopped.length > 0) {
|
|
127
148
|
log(`turn.${outcome}: stopped ${stopped.map((s) => s.name).join(", ")} (auto-snapshot; next turn auto-resumes)`);
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
/** Minimal structural sandbox the installer needs
|
|
1
|
+
/** Minimal structural sandbox the installer needs. `id` is OPTIONAL deliberately: eve 0.69.0's
|
|
2
|
+
* `prepare` hands over a `SandboxSession`, which carries no `id` at all, and `installAgentBrowser`
|
|
3
|
+
* reads only `run` โ `sandbox.id` appears once in the vendor bundle, inside `runAgentBrowser` (the
|
|
4
|
+
* TOOL path). Keeping it required would make the caller's cast load-bearing and hide the day a real
|
|
5
|
+
* `id` dependency comes back. @agent-browser/eve's own `EveSandboxSession` still requires it, so the
|
|
6
|
+
* one cast at the `installAgentBrowser` boundary stays and is commented there. */
|
|
2
7
|
export interface BakeableSandbox {
|
|
3
|
-
readonly id
|
|
8
|
+
readonly id?: string;
|
|
4
9
|
run(options: {
|
|
5
10
|
readonly command: string;
|
|
6
11
|
}): PromiseLike<{
|
|
@@ -10,6 +15,7 @@ export interface BakeableSandbox {
|
|
|
10
15
|
}>;
|
|
11
16
|
}
|
|
12
17
|
export interface AgentBrowserSandboxHooks {
|
|
18
|
+
/** ๐ด DEAD SINCE eve 0.69.0 (OPE-2247) โ nothing reads it. See this file's header. */
|
|
13
19
|
readonly revalidationKey: () => string;
|
|
14
20
|
readonly bootstrap: (input: {
|
|
15
21
|
use: () => Promise<BakeableSandbox>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../agent/browser/sandbox.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../agent/browser/sandbox.ts"],"names":[],"mappings":"AAgBA;;;;;mFAKmF;AACnF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,OAAO,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,WAAW,CAAC;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC7I;AAED,MAAM,WAAW,wBAAwB;IACvC,sFAAsF;IACtF,QAAQ,CAAC,eAAe,EAAE,MAAM,MAAM,CAAC;IACvC,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE;QAAE,GAAG,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvF;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAOtC,OAAO,GAAE,CAAC,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,OAAO,CAAkE,GACvH,wBAAwB,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAelD"}
|