@oxy-hq/sdk 2.11.0 → 2.13.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 (42) hide show
  1. package/README.md +44 -4
  2. package/dist/function-context-D8eyZuw_.d.cts +720 -0
  3. package/dist/function-context-D8eyZuw_.d.cts.map +1 -0
  4. package/dist/function-context-D8eyZuw_.d.mts +720 -0
  5. package/dist/function-context-D8eyZuw_.d.mts.map +1 -0
  6. package/dist/index.cjs +217 -11
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.d.cts +499 -638
  9. package/dist/index.d.cts.map +1 -1
  10. package/dist/index.d.mts +499 -638
  11. package/dist/index.d.mts.map +1 -1
  12. package/dist/index.mjs +215 -12
  13. package/dist/index.mjs.map +1 -1
  14. package/dist/ops.cjs +85 -0
  15. package/dist/ops.cjs.map +1 -0
  16. package/dist/ops.d.cts +61 -0
  17. package/dist/ops.d.cts.map +1 -0
  18. package/dist/ops.d.mts +61 -0
  19. package/dist/ops.d.mts.map +1 -0
  20. package/dist/ops.mjs +79 -0
  21. package/dist/ops.mjs.map +1 -0
  22. package/dist/{react-riTxd9ce.cjs → react-CkAQg9wB.cjs} +72 -7
  23. package/dist/react-CkAQg9wB.cjs.map +1 -0
  24. package/dist/{react-DqnINwTi.mjs → react-Cq3xULOr.mjs} +72 -7
  25. package/dist/react-Cq3xULOr.mjs.map +1 -0
  26. package/dist/{react-CLONxcnA.d.cts → react-D-Sf973d.d.cts} +94 -2
  27. package/dist/react-D-Sf973d.d.cts.map +1 -0
  28. package/dist/{react-CLONxcnA.d.mts → react-D-Sf973d.d.mts} +94 -2
  29. package/dist/react-D-Sf973d.d.mts.map +1 -0
  30. package/dist/shell.cjs +68 -4
  31. package/dist/shell.cjs.map +1 -1
  32. package/dist/shell.d.cts +62 -7
  33. package/dist/shell.d.cts.map +1 -1
  34. package/dist/shell.d.mts +62 -7
  35. package/dist/shell.d.mts.map +1 -1
  36. package/dist/shell.mjs +66 -5
  37. package/dist/shell.mjs.map +1 -1
  38. package/package.json +13 -2
  39. package/dist/react-CLONxcnA.d.cts.map +0 -1
  40. package/dist/react-CLONxcnA.d.mts.map +0 -1
  41. package/dist/react-DqnINwTi.mjs.map +0 -1
  42. package/dist/react-riTxd9ce.cjs.map +0 -1
package/dist/index.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
 
2
- import { $ as loadCustomAppManifest, A as UseSemanticQueryOpts, B as FunctionError, C as UseProcedureRunInput, D as UseQueryOpts, E as UseQueryInput, F as useProcedureRun, G as apiErrorFromResponse, H as FunctionResult, I as useQuery, J as OxyAppFunctionManifest, K as interpretCustomAppError, L as useResolvedManifest, M as useAgentRun, N as useFunction, O as UseQueryResult, P as useOxyApp, Q as _resetCustomAppManifestCacheForTest, R as useSemanticQuery, S as UseFunctionResult, T as UseProcedureRunResult, U as CustomAppErrorReport, V as FunctionLog, W as OxyApiError, X as OxyAppPerformanceManifest, Y as OxyAppManifest, Z as ResolvedCustomAppManifest, _ as SemanticFilter, a as AppFetcher, b as UseAgentRunInput, c as OxyAppProvider, d as OxyChatProps, f as ProcedureProgress, g as SemanticDateRangeOp, h as SemanticArrayOp, i as AgentSqlArtifact, j as UseSemanticQueryResult, k as UseSemanticQueryInput, l as OxyAppProviderProps, m as ProcedureRunState, n as AgentRunEvent, o as OxyAnswer, p as ProcedureResult, q as LoadManifestOptions, r as AgentRunState, s as OxyAnswerProps, t as AgentArtifact, u as OxyChat, v as SemanticScalarOp, w as UseProcedureRunOpts, x as UseAgentRunResult, y as SemanticTimeDimension, z as useTrackEvent } from "./react-CLONxcnA.mjs";
2
+ import { $ as loadCustomAppManifest, A as UseSemanticQueryOpts, B as FunctionError, C as UseProcedureRunInput, D as UseQueryOpts, E as UseQueryInput, F as useProcedureRun, G as apiErrorFromResponse, H as FunctionResult, I as useQuery, J as OxyAppFunctionManifest, K as interpretCustomAppError, L as useResolvedManifest, M as useAgentRun, N as useFunction, O as UseQueryResult, P as useOxyApp, Q as _resetCustomAppManifestCacheForTest, R as useSemanticQuery, S as UseFunctionResult, T as UseProcedureRunResult, U as CustomAppErrorReport, V as FunctionLog, W as OxyApiError, X as OxyAppPerformanceManifest, Y as OxyAppManifest, Z as ResolvedCustomAppManifest, _ as SemanticFilter, a as AppFetcher, b as UseAgentRunInput, c as OxyAppProvider, d as OxyChatProps, f as ProcedureProgress, g as SemanticDateRangeOp, h as SemanticArrayOp, i as AgentSqlArtifact, j as UseSemanticQueryResult, k as UseSemanticQueryInput, l as OxyAppProviderProps, m as ProcedureRunState, n as AgentRunEvent, o as OxyAnswer, p as ProcedureResult, q as LoadManifestOptions, r as AgentRunState, s as OxyAnswerProps, t as AgentArtifact, u as OxyChat, v as SemanticScalarOp, w as UseProcedureRunOpts, x as UseAgentRunResult, y as SemanticTimeDimension, z as useTrackEvent } from "./react-D-Sf973d.mjs";
3
+ import { C as StorageDownloadUrl, D as StoragePutResult, E as StoragePutOptions, O as StorageUploadUrl, S as OxyWarehouseApi, T as StorageObject, _ as OxyReach, a as OxyEmailApi, b as OxyStorageApi, c as OxyFunctionHandler, d as OxyFunctionUser, f as OxyIdentityKind, g as OxyOrgTeam, h as OxyOrgPlace, i as OxyAirwayApi, k as StorageUploadUrlInput, l as OxyFunctionRequest, m as OxyOrgAssignment, n as EmailSendInput, o as OxyFetchResult, p as OxyOltpApi, r as EmailSendResult, s as OxyFunctionContext, t as EmailAttachment, u as OxyFunctionRow, v as OxySecretsApi, w as StorageListPage, x as OxyTransaction, y as OxySemanticApi } from "./function-context-D8eyZuw_.mjs";
3
4
  //#region src/config.d.ts
4
5
  /**
5
6
  * Configuration for the Oxy SDK
@@ -39,12 +40,170 @@ interface OxyConfig {
39
40
  disableAutoAuth?: boolean;
40
41
  }
41
42
  //#endregion
43
+ //#region src/peerCohort.d.ts
44
+ /** How a subject's benchmark is computed from its peers. Serde snake_case:
45
+ * `"median"` | `"p75"` | `"best_peer"`. */
46
+ type BenchmarkStatistic = "median" | "p75" | "best_peer";
47
+ /**
48
+ * Why a subject was left out of the comparison entirely — distinct from
49
+ * `sufficient: false` on a {@link CohortSubject}, which still gets a computed
50
+ * comparison. This is the fix for a reference implementation where an excluded
51
+ * store "simply did not appear in the list and no screen said why": every
52
+ * exclusion here carries a reason, and a consumer MUST render these rather
53
+ * than silently drop them.
54
+ */
55
+ interface ExcludedSubject {
56
+ /** For a NULL entity key, a synthesized stable id of the form
57
+ * `"(null) [<require values>]"` rather than an empty string. */
58
+ key: string;
59
+ /** Human-readable prose explaining the exclusion — NOT a coded enum, so
60
+ * render it verbatim rather than switching on it. */
61
+ reason: string;
62
+ }
63
+ /**
64
+ * One subject's comparison against its peer cohort.
65
+ *
66
+ * `sufficient` reflects whether `peer_count` met the cohort's declared
67
+ * `min_peers`, but `min_peers` is a REPORTING predicate, never a filter: a
68
+ * subject below the threshold is still returned with `baseline` and `gap`
69
+ * computed from whatever peers it has. Never filter on `sufficient`
70
+ * client-side — surface it so the caller can decide whether to act on a
71
+ * comparison built from a thin peer set.
72
+ */
73
+ interface CohortSubject {
74
+ key: string;
75
+ value: number;
76
+ /** The peer benchmark value. `0.0` when `peer_count === 0` — this is NOT a
77
+ * baseline of zero, there simply is none. Check `peer_count` before
78
+ * treating `baseline` as meaningful. */
79
+ baseline: number;
80
+ /** Oriented so a positive value always means opportunity, regardless of
81
+ * whether the underlying measure is "bigger is better" or the reverse.
82
+ * `0.0` when there are no peers to compare against. */
83
+ gap: number;
84
+ /** Non-reciprocal: appearing in another subject's `peers` does not imply
85
+ * this subject lists them back. */
86
+ peers: string[];
87
+ peer_count: number;
88
+ /** `peer_count >= cohort's min_peers`. A reporting flag, not a gate — see
89
+ * the type-level doc above. */
90
+ sufficient: boolean;
91
+ }
92
+ /**
93
+ * Result of resolving one subject's peer cohort and every peer's comparison
94
+ * against it.
95
+ */
96
+ interface PeerCohortResult {
97
+ entity: string;
98
+ cohort: string;
99
+ measure: string;
100
+ statistic: BenchmarkStatistic;
101
+ /** `[start, end]` inclusive date strings for the measured period. */
102
+ period: [string, string];
103
+ /**
104
+ * The window peers were drawn from, when the cohort declares a peer band.
105
+ * `Some(period)` when the cohort declares a band with no explicit
106
+ * `window:` of its own; absent only when the cohort declares no band at
107
+ * all. Anchored at `period`'s START and extended backward, so it always
108
+ * CONTAINS `period` — never a disjoint comparison window.
109
+ *
110
+ * `| null` because this mirrors an `Option<(String, String)>` on a
111
+ * GIT-PINNED struct: `skip_serializing_if` is a serde attribute today, not
112
+ * a guarantee, so a reader must accept both encodings. Compare with
113
+ * `!= null`, never `!== undefined`.
114
+ */
115
+ band_window?: [string, string] | null;
116
+ subjects: CohortSubject[];
117
+ excluded: ExcludedSubject[];
118
+ }
119
+ /**
120
+ * Request to resolve a peer cohort. `cohort` and `band_window` are echoed
121
+ * back on {@link PeerCohortResult} specifically so a UI rendering them cannot
122
+ * drift from the query that produced them.
123
+ */
124
+ interface CohortRequest {
125
+ /**
126
+ * The bare name of an `entities:` entry in the semantic model — e.g.
127
+ * `"restaurant_id"` — NOT a column name and NOT a qualified `view.entity`
128
+ * (`"stores.store"`). The server compares this verbatim against the
129
+ * entity half of a measure's `default_cohort: "entity.cohort_name"` and
130
+ * against `Entity::name` when locating the bound view; a qualified name
131
+ * fails both checks and a scoped caller gets `403 cohort_scope_unavailable`.
132
+ */
133
+ entity: string;
134
+ measure: string;
135
+ time_dimension: string;
136
+ /** `[start, end]` inclusive date strings. */
137
+ period: [string, string];
138
+ /** Falls back to the measure's `default_cohort:` when omitted. */
139
+ cohort?: string;
140
+ /** Defaults to `"median"` server-side when omitted. */
141
+ statistic?: string;
142
+ }
143
+ /**
144
+ * Shape of the inner request helper exposed by `OxyClient`. The peer-cohort
145
+ * client reuses it to inherit auth headers, timeout, baseUrl, and project
146
+ * scoping rather than reimplementing fetch end-to-end.
147
+ */
148
+ type RequestFn$2 = <T>(endpoint: string, options?: RequestInit) => Promise<T>;
149
+ /**
150
+ * Client for the `/semantic/cohort` endpoint. Surfaces airlayer's peer-cohort
151
+ * benchmarking — comparing an entity's subjects against their declared peers
152
+ * on a measure, with exclusions explained rather than silently dropped.
153
+ *
154
+ * Construction is internal to {@link OxyClient} — call `client.peerCohort`
155
+ * to access an instance rather than building one yourself.
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * const client = await OxyClient.create({ projectId: "...", apiKey: "..." });
160
+ * const result = await client.peerCohort.resolve({
161
+ * entity: "restaurant_id",
162
+ * measure: "orders.net_revenue",
163
+ * time_dimension: "orders.order_date",
164
+ * period: ["2025-09-01", "2025-09-30"],
165
+ * });
166
+ * for (const excluded of result.excluded) {
167
+ * console.warn(excluded.key, excluded.reason);
168
+ * }
169
+ * ```
170
+ */
171
+ declare class PeerCohortClient {
172
+ private readonly request;
173
+ private readonly config;
174
+ constructor(config: OxyConfig, request: RequestFn$2);
175
+ private path;
176
+ private buildQuery;
177
+ /**
178
+ * Resolve a subject's peer cohort and every peer's comparison against it.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * const result = await client.peerCohort.resolve({
183
+ * entity: "restaurant_id",
184
+ * measure: "orders.net_revenue",
185
+ * time_dimension: "orders.order_date",
186
+ * period: ["2025-09-01", "2025-09-30"],
187
+ * });
188
+ * ```
189
+ */
190
+ resolve(request: CohortRequest): Promise<PeerCohortResult>;
191
+ }
192
+ //#endregion
42
193
  //#region src/metricTree.d.ts
43
194
  type EdgeKind = "component" | "driver";
44
195
  type DriverDirection = "positive" | "negative" | "unknown";
45
196
  type DriverStrength = "strong" | "moderate" | "weak";
46
197
  type DriverConfidence = "high" | "medium" | "low";
47
- type DriverForm = "linear" | "log-log" | "log-linear" | "linear-log";
198
+ /** The shape of a driver relationship.
199
+ *
200
+ * The THIRD hand-maintained mirror of this enum (airlayer's is canonical,
201
+ * `web-app/src/types/metricTree.ts` is the second). Nothing enforces that they
202
+ * agree, and the last time one fell behind — `oxy-semantic`, five variants
203
+ * short — a valid `.view.yml` stopped parsing. A type-only union fails more
204
+ * softly: an SDK consumer reading a tree with a quadratic edge just gets a
205
+ * union that cannot hold it. Add new shapes here whenever airlayer grows one. */
206
+ type DriverForm = "linear" | "log-log" | "log-linear" | "linear-log" | "quadratic" | "cubic" | "sqrt" | "inverse" | "linear-log-quadratic";
48
207
  interface MetricNode {
49
208
  id: string;
50
209
  view: string;
@@ -53,6 +212,11 @@ interface MetricNode {
53
212
  description?: string | null;
54
213
  measure_type: string;
55
214
  is_composite: boolean;
215
+ /** Whether this measure can be drilled into. Serialized rather than
216
+ * re-derived: `measure_type` misses eligible composites, and edge presence
217
+ * over-admits nested / cross-view / multiplicative passthroughs the engine
218
+ * refuses. Non-optional — it is on every metric-tree response. */
219
+ drillable: boolean;
56
220
  expr?: string | null;
57
221
  }
58
222
  interface MetricEdge {
@@ -61,11 +225,18 @@ interface MetricEdge {
61
225
  kind: EdgeKind;
62
226
  /** Sign of a component edge; omitted (defaults to +1) for most edges. */
63
227
  sign?: number;
228
+ /** Arithmetic operator joining a component child to its parent. Omitted
229
+ * when it is `add` — airlayer skips the field at its default — so absent
230
+ * MEANS `add`, never "unknown". Only `mul` / `div` are multiplicative;
231
+ * `add` / `sub` propagate exactly. */
232
+ operator?: "add" | "sub" | "mul" | "div";
64
233
  direction: DriverDirection;
65
234
  strength: DriverStrength;
66
235
  confidence: DriverConfidence;
67
236
  coefficient?: number | null;
68
237
  form: DriverForm;
238
+ /** Whether `form` was declared in the YAML or inferred by the fit. */
239
+ form_declared?: boolean;
69
240
  intercept?: number | null;
70
241
  lag?: number | null;
71
242
  description?: string | null;
@@ -75,6 +246,10 @@ interface MetricTree {
75
246
  nodes: MetricNode[];
76
247
  edges: MetricEdge[];
77
248
  root?: string | null;
249
+ /** Refusals raised while building the tree — a driver declaring both
250
+ * `coefficient:` and `coefficients:`, or a wrong-width vector. Absent when
251
+ * empty, so a lever that moves nothing still has a way to say why. */
252
+ warnings?: string[];
78
253
  }
79
254
  interface SensitivityDriver {
80
255
  measure: string;
@@ -107,6 +282,191 @@ interface PredictResult {
107
282
  inputs: PredictChange[];
108
283
  impacts: PredictImpact[];
109
284
  }
285
+ /** node_id → the measure's value over the baseline window. */
286
+ type MeasureValues = Record<string, number>;
287
+ /** A driver edge's coefficient, measured from history by the baseline query.
288
+ *
289
+ * Either `coefficient` is set or `refusal` is — never both, never neither.
290
+ * A refusal is a result: it is why a measure downstream of the change shows
291
+ * no number. Echo the whole array into `predict` verbatim, refusals included;
292
+ * the server ignores entries carrying no coefficient, and filtering them here
293
+ * would be a second place for the two sides to disagree. */
294
+ interface FittedDriver {
295
+ from: string;
296
+ to: string;
297
+ lag?: number;
298
+ /** The form the slope was measured in. The same number reads as dollars per
299
+ * dollar under `linear` and as a percent-per-percent elasticity under
300
+ * `log-log`, so a bare figure has no unit. */
301
+ form?: DriverForm;
302
+ /** Paired observations behind the fit.
303
+ *
304
+ * `| null` because this mirrors an `Option<f64>` on a GIT-PINNED struct:
305
+ * `skip_serializing_if` is a serde attribute today, not a guarantee, so a
306
+ * reader must accept both encodings. Compare with `!= null`, never
307
+ * `!== undefined` — and the type has to admit both, or the safe read looks
308
+ * like dead code. Same rule on `t_stat`, `t_stats`, `se_terms` and
309
+ * `coefficient` below. */
310
+ n?: number | null;
311
+ n_panels?: number;
312
+ n_nonpositive?: number;
313
+ /** The FIRST basis term — the whole answer for a single-term form; for a
314
+ * shape that can turn it is only the slope, so read `coefficients`.
315
+ * `| null` because it is an `Option<f64>` on the wire. */
316
+ coefficient?: number | null;
317
+ /** One coefficient per basis term, in basis order. This is what propagation
318
+ * evaluates. */
319
+ coefficients?: number[];
320
+ se?: number;
321
+ /** Elements `| null` for the reason stated on `n`. */
322
+ se_terms?: (number | null)[];
323
+ /** `| null` for the reason stated on `n`. */
324
+ t_stat?: number | null;
325
+ /** `t` per basis term, in basis order — `[1]` is the second basis term, the
326
+ * squared one under every shape that can turn. Elements `| null` for the
327
+ * reason stated on `n`. */
328
+ t_stats?: (number | null)[];
329
+ /** Sufficient statistics of the basis over the rows the fit used. Not
330
+ * diagnostic: the fit is per row and a change is a window aggregate, and a
331
+ * curved response cannot cross that gap without these. Echo verbatim. */
332
+ moments?: {
333
+ n?: number;
334
+ s1?: number;
335
+ s2?: number;
336
+ };
337
+ /** `[min, max]` driver values observed. A change beyond this spread is
338
+ * refused rather than extrapolated. */
339
+ domain?: [number, number];
340
+ /** The response sampled as `[change fraction, delta]`. Read this instead of
341
+ * interpreting the coefficients — peak, break-even and saturation are all
342
+ * properties of these samples, so a reader written against them keeps
343
+ * working when a new shape is added. */
344
+ profile?: [number, number][];
345
+ form_source?: "declared" | "inferred";
346
+ /** Every shape considered, scored comparably (AIC in y-space, lower better).
347
+ * Empty when the form was declared. `all_terms_significant` false means the
348
+ * candidate was never eligible, however good its score. */
349
+ candidates?: {
350
+ form: DriverForm;
351
+ aic: number;
352
+ all_terms_significant: boolean;
353
+ }[];
354
+ refusal?: string;
355
+ }
356
+ /** Why a reachable node has no baseline value. */
357
+ interface UnvaluedNode {
358
+ id: string;
359
+ reason?: string | null;
360
+ }
361
+ /** Narrow the baseline to one world-model instance. Omit to value the whole
362
+ * population. */
363
+ interface BaselineInstance {
364
+ entity: string;
365
+ /** JSON array for a composite key, else a bare scalar. */
366
+ key: string;
367
+ }
368
+ interface BaselineRequest {
369
+ /** The nodes you intend to change. Values are fetched for these plus
370
+ * everything forward-reachable from them — not the whole tree. */
371
+ roots: string[];
372
+ time_dimension: string;
373
+ /** `[start, end]` inclusive date strings. */
374
+ period: [string, string];
375
+ instance?: BaselineInstance | null;
376
+ }
377
+ interface BaselineResponse {
378
+ values: MeasureValues;
379
+ unvalued: UnvaluedNode[];
380
+ resolved_period: [string, string];
381
+ /** Why the baseline produced no values, in words worth showing. Absent when
382
+ * measures were valued normally. */
383
+ baseline_note?: string | null;
384
+ /** Coefficients fitted for driver edges that declare none, plus refusals.
385
+ * Absent when every reachable driver edge already declares one. */
386
+ fitted?: FittedDriver[];
387
+ }
388
+ interface PredictOptions {
389
+ /** Current values for the measures involved. Supplying them lets
390
+ * multiplicative edges be sized instead of returned `unquantifiable`. */
391
+ values?: MeasureValues;
392
+ /** The baseline's `fitted` array, verbatim. */
393
+ coefficients?: FittedDriver[];
394
+ }
395
+ type ProjectionGranularity = "day" | "week" | "month";
396
+ /**
397
+ * The scenario's time axis. `baseline` answers "what is this measure worth
398
+ * over the window"; this answers "what has it been doing, and what does it do
399
+ * next".
400
+ *
401
+ * The history window is deliberately its own, NOT the baseline's: the
402
+ * forecaster refuses anything under eight seasonal cycles (56 daily buckets,
403
+ * 32 weekly, 24 monthly), and a 30-day scenario baseline reused here would
404
+ * make "no forecast" the normal answer.
405
+ */
406
+ interface ProjectionRequest {
407
+ /** Lever node ids. Curves are drawn for these plus everything
408
+ * forward-reachable from them — the same set {@link BaselineRequest} values. */
409
+ roots: string[];
410
+ time_dimension: string;
411
+ /** `[start, end]` inclusive date strings for the HISTORY. */
412
+ period: [string, string];
413
+ /** Narrow to one world-model instance. Omit to project the whole
414
+ * population — same picker the baseline uses. */
415
+ instance?: BaselineInstance | null;
416
+ /** Bucket width. Defaults to `day` server-side. */
417
+ granularity?: ProjectionGranularity;
418
+ /** Buckets to project past the last historical one. 1..=365; outside that
419
+ * it is a 400, never a silent clamp — a horizon quietly truncated reads as
420
+ * a forecast that genuinely ends in March. */
421
+ horizon: number;
422
+ /** Seasonal periods, in buckets, applied to every measure in the request.
423
+ *
424
+ * Omitting it is not "use the default" — it means *resolve per measure*
425
+ * from whatever `.monitor.yml` already watches that series, which is what
426
+ * keeps this band the band an anomaly had to breach. Send it only to pin a
427
+ * cycle nobody has declared. Each period must be >= 2; `[]` is a 400. */
428
+ seasonality?: number[];
429
+ }
430
+ interface HistoryPoint {
431
+ /** Bucket start, `YYYY-MM-DD`. */
432
+ date: string;
433
+ value: number;
434
+ }
435
+ interface ForecastPoint {
436
+ date: string;
437
+ point: number;
438
+ /** The prediction interval. `null` / absent means the model returned no
439
+ * band — unknown spread, NOT a band of zero width. Never collapse these
440
+ * onto `point`: a zero-width band is a claim of certainty nobody made. */
441
+ lower?: number | null;
442
+ upper?: number | null;
443
+ }
444
+ /** One measure's baseline curve: what happened, then what comes next.
445
+ *
446
+ * An empty `forecast` carrying a `refusal` is a state, not a gap — most often
447
+ * "too little history to fit", or the warehouse refusing this one measure. It
448
+ * must never render as a flat forward line, which is what any code defaulting
449
+ * the missing curve to "unchanged" would draw. */
450
+ interface MeasureProjection {
451
+ measure: string;
452
+ history: HistoryPoint[];
453
+ forecast: ForecastPoint[];
454
+ refusal?: string | null;
455
+ /** The seasonal periods this curve was decomposed against — resolved per
456
+ * measure, so two series in one response can legitimately differ. */
457
+ seasonality: number[];
458
+ }
459
+ interface ProjectionResponse {
460
+ granularity: ProjectionGranularity;
461
+ /** Echoed back: the query is expensive and callers cache on it. */
462
+ resolved_period: [string, string];
463
+ horizon: number;
464
+ series: MeasureProjection[];
465
+ /** Why the WHOLE projection is empty, when it is. Absent when at least one
466
+ * measure produced history — a partial failure is each measure's own
467
+ * `refusal`, never a banner over curves that are drawing fine. */
468
+ projection_note?: string | null;
469
+ }
110
470
  type SplitKind = {
111
471
  type: "component";
112
472
  child_measure: string;
@@ -243,6 +603,17 @@ interface OpportunityRequest {
243
603
  target: string;
244
604
  time_dimension: string;
245
605
  period: [string, string];
606
+ /**
607
+ * How a segment's benchmark is computed from its peers. Defaults to
608
+ * `"p75"` server-side when omitted.
609
+ *
610
+ * An airlayer upgrade deleted the engine's adaptive selection — best-peer
611
+ * for a dimension with too few segments for a percentile to mean anything,
612
+ * p75 once there were enough — in favor of an explicit required argument.
613
+ * No single fixed value reproduced the old behavior, so rather than
614
+ * freezing one server-side, the choice is now the caller's.
615
+ */
616
+ statistic?: BenchmarkStatistic;
246
617
  }
247
618
  interface OpportunityResult {
248
619
  target: string;
@@ -280,9 +651,10 @@ interface TimeDimensionsResponse {
280
651
  */
281
652
  type RequestFn$1 = <T>(endpoint: string, options?: RequestInit) => Promise<T>;
282
653
  /**
283
- * Client for the `/semantic/metric-tree*` endpoints. Surfaces the four
284
- * airlayer metric-tree analyses (tree introspection, sensitivity, predict,
285
- * explain, opportunity) over typed methods.
654
+ * Client for the `/semantic/metric-tree*` endpoints. Surfaces the airlayer
655
+ * metric-tree analyses — tree introspection, sensitivity, explain, opportunity
656
+ * — plus the three legs of scenario forecasting (`baseline` levels,
657
+ * `predict` propagation, `projection` curves) over typed methods.
286
658
  *
287
659
  * Construction is internal to {@link OxyClient} — call `client.metricTree`
288
660
  * to access an instance rather than building one yourself.
@@ -327,10 +699,45 @@ declare class MetricTreeClient {
327
699
  * ```
328
700
  */
329
701
  getSensitivity(measureId: string): Promise<SensitivityResult>;
702
+ /**
703
+ * Value a change's starting point, and measure the coefficients it needs.
704
+ *
705
+ * Two warehouse reads: the current value of every node reachable from
706
+ * `roots`, and — for driver edges that declare no `coefficient:` — a fit
707
+ * over the window. Both are expensive, which is why they live here and not
708
+ * in `predict`: `predict` is database-free by design so it can re-run per
709
+ * keystroke, and it CANNOT measure a coefficient itself.
710
+ *
711
+ * That is the whole reason to call this. Pass `fitted` back into `predict`
712
+ * and an undeclared edge propagates; omit it and `predict` has nothing to
713
+ * multiply by, so the impact is simply absent — no error, no refusal, just a
714
+ * downstream measure that never appears.
715
+ *
716
+ * @example
717
+ * ```typescript
718
+ * const baseline = await client.metricTree.getBaseline({
719
+ * roots: ["marketing_spend.total_spend"],
720
+ * time_dimension: "orders.order_date",
721
+ * period: ["2025-09-01", "2025-09-30"],
722
+ * });
723
+ * const result = await client.metricTree.predict(
724
+ * [{ measure: "marketing_spend.total_spend", delta: 10000 }],
725
+ * { values: baseline.values, coefficients: baseline.fitted }
726
+ * );
727
+ * ```
728
+ */
729
+ getBaseline(request: BaselineRequest): Promise<BaselineResponse>;
330
730
  /**
331
731
  * Propagate hypothetical `(measure, delta)` changes upward through the
332
732
  * tree. Returns the estimated impact on every downstream measure.
333
733
  *
734
+ * Database-free, so it re-runs cheaply — and so it can only use
735
+ * coefficients it is GIVEN. Without `options.coefficients` from
736
+ * {@link getBaseline}, every edge whose `.view.yml` declares no
737
+ * `coefficient:` contributes nothing and its downstream measures are
738
+ * silently missing from `impacts`. Without `options.values`, multiplicative
739
+ * component edges come back `unquantifiable` rather than sized.
740
+ *
334
741
  * @example
335
742
  * ```typescript
336
743
  * const result = await client.metricTree.predict([
@@ -338,7 +745,37 @@ declare class MetricTreeClient {
338
745
  * ]);
339
746
  * ```
340
747
  */
341
- predict(changes: PredictChange[]): Promise<PredictResult>;
748
+ predict(changes: PredictChange[], options?: PredictOptions): Promise<PredictResult>;
749
+ /**
750
+ * Draw the scenario's time axis: bucketed history for the levers and
751
+ * everything downstream, plus the forward curve the detector's own model
752
+ * expects next.
753
+ *
754
+ * The third leg of scenario forecasting. {@link getBaseline} gives levels
755
+ * and coefficients, {@link predict} propagates a change with no database at
756
+ * all, and this gives time — one warehouse query, so treat it like the
757
+ * baseline: fetch on a window change, not on a lever edit.
758
+ *
759
+ * **Returns the BASELINE curve only.** The scenario's second curve is
760
+ * arithmetic over this and a `predict` result — a proportional shift landing
761
+ * `lag` buckets in — and is composed client-side deliberately, so editing a
762
+ * lever costs no query.
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * const projection = await client.metricTree.getProjection({
767
+ * roots: ["marketing_spend.total_spend"],
768
+ * time_dimension: "orders.order_date",
769
+ * period: ["2024-09-01", "2025-08-31"],
770
+ * granularity: "day",
771
+ * horizon: 30,
772
+ * });
773
+ * for (const series of projection.series) {
774
+ * if (series.refusal) console.warn(series.measure, series.refusal);
775
+ * }
776
+ * ```
777
+ */
778
+ getProjection(request: ProjectionRequest): Promise<ProjectionResponse>;
342
779
  /**
343
780
  * Period-over-period root-cause decomposition. Recursively splits the
344
781
  * target measure by components and dimensions until the move concentrates.
@@ -735,636 +1172,6 @@ interface CustomAppDebugSnapshot {
735
1172
  */
736
1173
  declare function getCustomAppDebug(resolved: ResolvedCustomAppManifest): Promise<CustomAppDebugSnapshot>;
737
1174
  //#endregion
738
- //#region src/custom-app/function-context.d.ts
739
- /**
740
- * The request passed as the first argument to a function's default export.
741
- *
742
- * The host hands the isolate the raw request body as a string (see
743
- * `req_json` in `runtime.rs`); parse it yourself, e.g.
744
- * `JSON.parse(req.body || "{}")`. This is intentionally *not* a full Web
745
- * `Request` — there is no `.json()` / headers object in v1.
746
- */
747
- interface OxyFunctionRequest {
748
- /** Raw request body as received (JSON string for a JSON POST). */
749
- body: string;
750
- }
751
- /** A single row from a `ctx.query` / `ctx.queryStream` result. */
752
- type OxyFunctionRow = Record<string, unknown>;
753
- /** One org team the caller belongs to, as reported by {@link OxyFunctionUser.teams}. */
754
- interface OxyOrgTeam {
755
- id: string;
756
- name: string;
757
- }
758
- /**
759
- * Who — or what — invoked this function.
760
- *
761
- * `"system"` means **no caller to attribute this to** — not necessarily "no
762
- * human caused it". A schedule tick, an Airway transform step, and an operator's
763
- * manual *Run now* all take this path: they run under the org owner's `id` (the
764
- * invocation record needs a real user FK) with every caller field absent. So on
765
- * a manual run a person really did click, and there is still no way to reach
766
- * them; the platform does not carry the triggering operator through the job
767
- * queue.
768
- *
769
- * Any branch that emails "the person who clicked" or renders a personal view
770
- * must check this rather than sniff the synthetic `email` — and must have a
771
- * sensible answer for the case where there is nobody to send to.
772
- */
773
- type OxyIdentityKind = "user" | "system";
774
- /**
775
- * Identity of the invoking user (route) or the system identity (schedule,
776
- * Airway step, or a manual job run).
777
- *
778
- * Assembled server-side on every invocation from the authenticated session —
779
- * **nothing on it is client-supplied**, which is the entire reason to read
780
- * identity here instead of from the request body. See
781
- * `internal-docs/custom-apps-user-identity.md` for the full contract, including
782
- * what the client-side `useShellContext()` can and cannot be trusted for.
783
- */
784
- interface OxyFunctionUser {
785
- /**
786
- * `users.id`. On a `"system"` invocation this is the org owner's id and not a
787
- * caller — check {@link kind} before attributing anything to it.
788
- */
789
- id: string;
790
- /**
791
- * Their email; `schedule+<fn>@system.oxy` when {@link kind} is `"system"`; and
792
- * **`null` for a frontline worker** — a crew member enrolled by PIN on a shared
793
- * device has no mailbox, and the platform stores none rather than inventing
794
- * one. That null is the one field that tells the crew from the office inside
795
- * a function, because a worker can never hold org membership (see
796
- * `orgRole`, which is absent for them too). Treat it as `string | null` in
797
- * app logic; it was typed `string` before the crew existed.
798
- */
799
- email: string | null;
800
- /**
801
- * The org that owns this app — the tenant boundary for anything the function
802
- * reads or writes.
803
- *
804
- * Servers before 2026-08-21 mistakenly sent this as `org_id`, so `orgId` read
805
- * `undefined` there; both keys are populated now. If your function filters SQL
806
- * on it, that is exactly the bug to re-check.
807
- */
808
- orgId: string;
809
- /** Display name. Absent on a `"system"` invocation. User-controlled free text —
810
- * fine for a greeting or an audit row, never a key, and escape it before it
811
- * reaches HTML or SQL. */
812
- name?: string;
813
- /** Avatar URL. Absent when unset or on a `"system"` invocation. */
814
- picture?: string;
815
- /**
816
- * The caller's role **within this app**, derived server-side from app
817
- * membership (with org-owner / Oxy-staff break-glass). Absent when they hold
818
- * no membership.
819
- *
820
- * This is the value to gate a privileged surface on — it cannot be forged by
821
- * the client, unlike a query param or a client-side flag:
822
- *
823
- * ```ts
824
- * if (ctx.user.appRole !== "admin") {
825
- * return Response.json({ error: "forbidden" }, { status: 403 });
826
- * }
827
- * ```
828
- *
829
- * Note it is deliberately NOT the org role: an app admin administers one app
830
- * without holding org-Admin (which also carries billing and member management).
831
- *
832
- * A `"system"` invocation runs under the org owner, so this reads `"admin"`
833
- * there — a schedule carries owner authority by construction. Add a
834
- * {@link kind} check when a surface must be human-only.
835
- */
836
- appRole?: "admin" | "member";
837
- /**
838
- * The caller's role in the owning **org**. Absent when they reach the app
839
- * without an org membership (Oxy staff on break-glass) or on a `"system"`
840
- * invocation.
841
- *
842
- * Informational, not a gate — org standing and app standing are separate
843
- * rings. Use it to explain ("ask your org admin to connect a warehouse"), to
844
- * label, or to route; gate on {@link appRole}.
845
- */
846
- orgRole?: "owner" | "admin" | "member";
847
- /**
848
- * The org teams the caller belongs to, name-sorted, and scoped to this app's
849
- * org — teams they hold in other orgs are never reported. Empty when they
850
- * belong to none.
851
- *
852
- * Optional because a server older than 2026-08-21 does not send it: use
853
- * `ctx.user.teams?.some(...)`, never `ctx.user.teams.some(...)`, or the
854
- * function throws on that server rather than degrading.
855
- *
856
- * Useful for *shaping* a view (default the Finance team to the finance tab).
857
- * Not a permission: a team only grants anything on an app through an app team
858
- * grant, which is already folded into {@link appRole}. Gating on a team name
859
- * invents a permission the platform cannot revoke.
860
- */
861
- teams?: OxyOrgTeam[];
862
- /**
863
- * Whether there is a caller to attribute this invocation to.
864
- *
865
- * On a current server this is exact — `ctx.user.kind === "system"` is the
866
- * check.
867
- *
868
- * Optional for the same reason as {@link teams}: a server older than
869
- * 2026-08-21 does not send it. Note there is no safe *inference* to fall back
870
- * on, in either direction — `=== "system"` reads `false` for a cron tick, and
871
- * `!== "user"` reads `true` for a real person. An older server genuinely
872
- * cannot tell you.
873
- *
874
- * So if you must support one, don't infer: a schedule invokes the function
875
- * with the `input` you configured on it, which is yours to mark.
876
- *
877
- * ```ts
878
- * const body = JSON.parse(req.body || "{}");
879
- * const isSystem = ctx.user.kind ? ctx.user.kind === "system" : body._trigger === "schedule";
880
- * ```
881
- */
882
- kind?: OxyIdentityKind;
883
- }
884
- /** Result of a `ctx.fetch` call. */
885
- interface OxyFetchResult {
886
- status: number;
887
- /** Response body, decoded per the requested {@link OxyFetchInit.encoding}. */
888
- body: string;
889
- /** Echoes how `body` was encoded (`"utf8"` unless base64 was requested). */
890
- encoding?: "utf8" | "base64";
891
- }
892
- /**
893
- * `init` for `ctx.fetch` — the standard `RequestInit` fields the host honours
894
- * (`method`, `headers`, `body`) plus how to decode the response.
895
- */
896
- type OxyFetchInit = RequestInit & {
897
- /**
898
- * How to decode the response body. `"utf8"` (default) is **lossy for
899
- * binary** — every non-UTF-8 byte becomes U+FFFD, so a fetched PDF/PNG comes
900
- * back corrupt. Pass `"base64"` for any binary response, e.g. to hand it
901
- * straight to an email attachment.
902
- */
903
- encoding?: "utf8" | "base64";
904
- };
905
- /**
906
- * `ctx.warehouse.*` — one of the app's configured databases by name.
907
- *
908
- * The writes require the database in the function's `destinations` allowlist;
909
- * `query` does not, because that allowlist is about modifying a project's
910
- * warehouse, and a `postgres_managed` database resolves the read-only analyst
911
- * for every caller regardless.
912
- */
913
- interface OxyWarehouseApi {
914
- /**
915
- * Read from a named database.
916
- *
917
- * `ctx.query` only ever reaches the project's DEFAULT database, so this is
918
- * how an app reads its own per-org OLTP store, which sits beside whatever
919
- * warehouse the project analyses.
920
- */
921
- query(database: string, sql: string): Promise<{
922
- rows: OxyFunctionRow[];
923
- truncated: boolean;
924
- }>;
925
- insert(database: string, table: string, rows: OxyFunctionRow[]): Promise<unknown>;
926
- exec(database: string, sql: string): Promise<unknown>;
927
- upsert(database: string, table: string, rows: OxyFunctionRow[], conflictColumns: string[]): Promise<unknown>;
928
- }
929
- /**
930
- * The handle `ctx.tx` passes to your callback — a pinned connection with an
931
- * open transaction.
932
- *
933
- * Both methods take **bound parameters** (`$1`, `$2`, …). Never build SQL by
934
- * concatenating request data: `ctx.warehouse.exec` takes a bare string, but a
935
- * transaction exists for surfaces that accept end-user input, and placeholders
936
- * are the only thing that makes that safe.
937
- *
938
- * The handle is live only for the duration of the callback. Using it after the
939
- * callback returns throws — it is not a connection you can stash.
940
- */
941
- interface OxyTransaction {
942
- /** Run a row-returning statement (including `INSERT … RETURNING`). */
943
- query(sql: string, params?: unknown[]): Promise<OxyFunctionRow[]>;
944
- /** Run a statement for its effect; resolves to the number of rows affected. */
945
- exec(sql: string, params?: unknown[]): Promise<number>;
946
- }
947
- /**
948
- * `ctx.oltp` — read and WRITE the app's OWN per-org OLTP schema (`app_<writer>`)
949
- * on the managed Postgres tenant, and nothing else.
950
- *
951
- * This is the write half `ctx.warehouse` cannot give an app: for a
952
- * `postgres_managed` database `ctx.warehouse` resolves the read-only analyst
953
- * (org-wide read, the org's `raw_*` extracts included), so a write authenticates
954
- * and then fails `permission denied`. `ctx.oltp` resolves the app's **writer**
955
- * role instead — DML rights scoped to the one `app_<writer>` schema, so it is
956
- * narrower on reads (no `raw_*`) and finally writable.
957
- *
958
- * Gated by the fail-closed `oltp` manifest capability (`"oltp": { "enabled":
959
- * true }`) — a pure gate. The target schema is derived from the app's own slug
960
- * (`oltp-bookings` → `app_oltp_bookings`), never named in the manifest, so a
961
- * manifest cannot point `ctx.oltp` at another app's schema. The store must be
962
- * provisioned first (ask whoever operates the org). No database name is passed —
963
- * the app's own store is implicit.
964
- *
965
- * Both methods take **bound parameters** (`$1`, `$2`, …). Never build SQL by
966
- * concatenating request data — a booking form is exactly the surface that takes
967
- * end-user input, and placeholders are the only thing that makes it safe. Each
968
- * call auto-commits; a failed statement rolls back.
969
- *
970
- * **Cost:** each call opens its own connection to the tenant (a TCP + TLS
971
- * handshake, and a wake-up if the compute was idle) and its own transaction, so
972
- * a per-row loop pays that per row. Prefer one statement over many — a
973
- * multi-row `INSERT`, an `INSERT … SELECT`, or `INSERT … RETURNING` to avoid a
974
- * follow-up read — and reach for `ctx.oltp` a handful of times per request, not
975
- * in a hot loop.
976
- *
977
- * ```ts
978
- * const [row] = await ctx.oltp.query(
979
- * "INSERT INTO bookings (name, party_size) VALUES ($1, $2) RETURNING id",
980
- * [name, partySize],
981
- * );
982
- * ```
983
- */
984
- interface OxyOltpApi {
985
- /** Run a row-returning statement (including `INSERT … RETURNING`). */
986
- query(sql: string, params?: unknown[]): Promise<OxyFunctionRow[]>;
987
- /** Run a statement for its effect; resolves to the number of rows affected. */
988
- exec(sql: string, params?: unknown[]): Promise<number>;
989
- }
990
- /** `ctx.secrets` — write app-scoped secrets (gated by the `secrets.write` capability). */
991
- interface OxySecretsApi {
992
- set(key: string, value: string): Promise<void>;
993
- }
994
- /** `ctx.semantic` — airlayer-compiled semantic queries (inherits the pre-agg fast path). */
995
- interface OxySemanticApi {
996
- query(spec: Record<string, unknown>): Promise<unknown>;
997
- }
998
- /** `ctx.airway` — seed/await an Airway ELT pipeline run. */
999
- interface OxyAirwayApi {
1000
- run(pipelineRef: string, variables?: Record<string, unknown> | null): Promise<{
1001
- runId: string;
1002
- }>;
1003
- }
1004
- /**
1005
- * Input to `ctx.email.send`. Platform-injected: the sender mailbox (`from`) is
1006
- * platform-controlled and **not** an accepted field — passing it is a typed
1007
- * error. Provide `html` and/or `text` as the body (render a template to HTML
1008
- * with `render` from `@oxy-hq/sdk/email`).
1009
- */
1010
- interface EmailSendInput {
1011
- /** Recipient address(es). Required. */
1012
- to: string | string[];
1013
- /** CC address(es). */
1014
- cc?: string | string[];
1015
- /** BCC address(es). */
1016
- bcc?: string | string[];
1017
- /** Reply-To address — the only sender-identity field an author may set. */
1018
- replyTo?: string;
1019
- /** Subject line. Required. */
1020
- subject: string;
1021
- /** HTML body. Provide at least one of `html` / `text`. */
1022
- html?: string;
1023
- /** Plain-text body. Provide at least one of `html` / `text`. */
1024
- text?: string;
1025
- /**
1026
- * Optional idempotency key (≤256 chars). Accepted and validated in v1 but a
1027
- * no-op until the persisted idempotency table lands — adopt it now so
1028
- * background (retried) sends become exactly-once once it does.
1029
- */
1030
- idempotencyKey?: string;
1031
- /**
1032
- * Files to attach. Max 20 per send, and **10 MiB decoded in total** — SES
1033
- * caps a whole message near 40 MB, so for anything larger store the file with
1034
- * {@link OxyStorageApi} and email a presigned link instead of inlining it.
1035
- *
1036
- * `content` is base64 by default; for generated text set
1037
- * `encoding: "utf8"` and attach the string as-is.
1038
- */
1039
- attachments?: EmailAttachment[];
1040
- }
1041
- /** One attachment on {@link EmailSendInput}. */
1042
- interface EmailAttachment {
1043
- /** Filename shown to the recipient. Required; path separators are stripped. */
1044
- filename: string;
1045
- /**
1046
- * File contents, interpreted per {@link EmailAttachment.encoding} — base64 by
1047
- * default, which is the only way binary crosses the isolate boundary.
1048
- */
1049
- content: string;
1050
- /**
1051
- * How `content` is encoded. Defaults to `"base64"`.
1052
- *
1053
- * Use `"utf8"` to attach text the function just generated (CSV, JSON, HTML)
1054
- * — it needs no encoder and is byte-exact for non-ASCII. `btoa` is the wrong
1055
- * tool there: it encodes U+0080..U+00FF as *Latin1*, so accented text comes
1056
- * out as mojibake rather than as an error. For binary, take base64 straight
1057
- * from the source — `ctx.storage.get(key, { encoding: "base64" })` or
1058
- * `ctx.fetch(url, { encoding: "base64" })` — or {@link bytesToBase64} for a
1059
- * `Uint8Array` you built yourself.
1060
- */
1061
- encoding?: "base64" | "utf8";
1062
- /** MIME type; defaults to `application/octet-stream`. */
1063
- contentType?: string;
1064
- /** Render inline (e.g. an image referenced as `cid:<contentId>`) instead of as a download. */
1065
- inline?: boolean;
1066
- /** Content-ID for an inline part, referenced from the HTML body as `cid:<contentId>`. */
1067
- contentId?: string;
1068
- }
1069
- /** Result of a successful `ctx.email.send`. */
1070
- interface EmailSendResult {
1071
- /** Provider (SES) message id of the sent message. */
1072
- messageId: string;
1073
- }
1074
- /** `ctx.email` — send email (gated by the `email.send` capability). */
1075
- interface OxyEmailApi {
1076
- send(input: EmailSendInput): Promise<EmailSendResult>;
1077
- }
1078
- /** Input to `ctx.storage.getUploadUrl`. */
1079
- interface StorageUploadUrlInput {
1080
- /**
1081
- * Destination path inside the app's silo, e.g. `"uploads/q1-report.pdf"`.
1082
- * Segments are sanitized server-side and cannot escape the silo. Omit to use
1083
- * `filename`, which is placed under `uploads/`.
1084
- */
1085
- pathname?: string;
1086
- /** Shorthand for `pathname: "uploads/<filename>"`. */
1087
- filename?: string;
1088
- /** MIME type; bound into the presigned PUT signature. Inferred when omitted. */
1089
- contentType?: string;
1090
- /**
1091
- * Exact byte length of the upload, bound into the signature — S3 rejects a
1092
- * body of any other size. Capped by the server's upload ceiling (100 MiB by
1093
- * default).
1094
- */
1095
- contentLength: number;
1096
- /** Presign lifetime in seconds (default 900; max 604800 — SigV4's own limit). */
1097
- expiresInSeconds?: number;
1098
- }
1099
- /** A minted presigned upload. */
1100
- interface StorageUploadUrl {
1101
- /** Presigned PUT — the browser uploads the file bytes directly to this URL. */
1102
- url: string;
1103
- /**
1104
- * The stored key. Record it (e.g. on a row in your warehouse) — it is how you
1105
- * fetch, list or link to the asset later. A random suffix is added so two
1106
- * people uploading `report.pdf` don't collide.
1107
- */
1108
- key: string;
1109
- /** ISO-8601 expiry of the presigned URL. */
1110
- expiresAt: string;
1111
- /**
1112
- * Retention tag for this key, present only when your app declares a matching
1113
- * `storage.retention` rule in `oxy-app.json` (e.g. `"oxy-ttl=30d"`).
1114
- *
1115
- * **When present, the upload MUST send it as the `x-amz-tagging` header** — it
1116
- * is bound into the signature, so omitting it fails the PUT with a signature
1117
- * mismatch rather than storing an untagged object:
1118
- *
1119
- * ```ts
1120
- * const { url, tagging } = await ctx.storage.getUploadUrl({ ... });
1121
- * await fetch(url, {
1122
- * method: "PUT",
1123
- * body: file,
1124
- * headers: {
1125
- * "Content-Type": file.type,
1126
- * ...(tagging ? { "x-amz-tagging": tagging } : {}),
1127
- * },
1128
- * });
1129
- * ```
1130
- *
1131
- * Signing it is deliberate: a browser that could drop the header could opt any
1132
- * upload out of the app's own retention policy.
1133
- */
1134
- tagging?: string;
1135
- }
1136
- /** A minted presigned download. */
1137
- interface StorageDownloadUrl {
1138
- url: string;
1139
- expiresAt: string;
1140
- }
1141
- /** One asset in the app's silo. */
1142
- interface StorageObject {
1143
- key: string;
1144
- size: number;
1145
- contentType?: string | null;
1146
- /** ISO-8601. */
1147
- lastModified?: string | null;
1148
- }
1149
- /** One page of {@link OxyStorageApi.list}. */
1150
- interface StorageListPage {
1151
- objects: StorageObject[];
1152
- /** Pass back as `cursor` to fetch the next page; `null` when complete. */
1153
- cursor: string | null;
1154
- hasMore: boolean;
1155
- }
1156
- /** Options for {@link OxyStorageApi.put}. */
1157
- interface StoragePutOptions {
1158
- /** MIME type. Inferred from the pathname's extension when omitted. */
1159
- contentType?: string;
1160
- /**
1161
- * How `body` is encoded. `"base64"` is what makes **binary** generated assets
1162
- * (PDF, PNG, Parquet) possible — a UTF-8 string would corrupt them.
1163
- */
1164
- encoding?: "utf8" | "base64";
1165
- /** Append a short random component before the extension to avoid collisions. */
1166
- addRandomSuffix?: boolean;
1167
- /**
1168
- * Replace an existing asset at this path. Defaults to `false` — writing over
1169
- * an asset by accident is worse than an error, so this is opt-in.
1170
- */
1171
- allowOverwrite?: boolean;
1172
- /** `Cache-Control: max-age=<seconds>` stored on the object. */
1173
- cacheControlMaxAge?: number;
1174
- }
1175
- /** Result of a `put` (and of `copy`). */
1176
- interface StoragePutResult {
1177
- key: string;
1178
- size: number;
1179
- contentType: string;
1180
- }
1181
- /**
1182
- * `ctx.storage` — this app's **asset store**, covering both kinds of file an app
1183
- * produces, in one silo (`customer-app-storage/<app_id>/`):
1184
- *
1185
- * - **Uploaded** — a human picks a file; `getUploadUrl` mints a presigned PUT and
1186
- * the browser uploads **straight to S3**, so uploads aren't bounded by the
1187
- * request-body limit and the bytes never pass through your function.
1188
- * - **Generated** — your function produces the file (a rendered PDF, a CSV
1189
- * export, a chart PNG) and writes it with `put`, using
1190
- * `{ encoding: "base64" }` for binary.
1191
- *
1192
- * Gated by the fail-closed `storage.read` / `storage.write` capabilities in
1193
- * `oxy-app.json`. Every asset is private; reads are always presigned and
1194
- * time-boxed. Keys are confined to your app — another app's key is rejected.
1195
- *
1196
- * ```ts
1197
- * // Uploaded: mint a URL, browser PUTs to it, then record `key`.
1198
- * const { url, key } = await ctx.storage.getUploadUrl({
1199
- * filename: "q1-report.pdf", contentType: "application/pdf", contentLength: size,
1200
- * });
1201
- *
1202
- * // Generated: write a CSV your function just built.
1203
- * const { key } = await ctx.storage.put("generated/jan.csv", csv);
1204
- *
1205
- * // Either way: email a link that outlives the request.
1206
- * const { url: link } = await ctx.storage.getDownloadUrl(key, {
1207
- * expiresInSeconds: 604800, download: true,
1208
- * });
1209
- * ```
1210
- */
1211
- interface OxyStorageApi {
1212
- /** Mint a presigned PUT for a browser upload (requires `storage.write`). */
1213
- getUploadUrl(input: StorageUploadUrlInput): Promise<StorageUploadUrl>;
1214
- /**
1215
- * Mint a presigned GET (requires `storage.read`). `download: true` forces a
1216
- * save-as via `Content-Disposition`, which is what an emailed link wants.
1217
- */
1218
- getDownloadUrl(key: string, opts?: {
1219
- expiresInSeconds?: number;
1220
- download?: boolean;
1221
- }): Promise<StorageDownloadUrl>;
1222
- /**
1223
- * Write a generated asset (requires `storage.write`). Capped at 6 MiB — for
1224
- * anything larger, mint a presigned upload URL and stream to it.
1225
- */
1226
- put(pathname: string, body: string, opts?: StoragePutOptions): Promise<StoragePutResult>;
1227
- /** Read an asset back; `null` when absent (requires `storage.read`). */
1228
- get(key: string, opts?: {
1229
- encoding?: "utf8" | "base64";
1230
- }): Promise<{
1231
- body: string;
1232
- contentType: string | null;
1233
- size: number;
1234
- encoding: string;
1235
- } | null>;
1236
- /** Metadata without the body; `null` when absent (requires `storage.read`). */
1237
- head(key: string): Promise<StorageObject | null>;
1238
- /**
1239
- * One page of assets (requires `storage.read`). Paginated deliberately — pass
1240
- * the returned `cursor` back to walk a large silo without loading it all.
1241
- */
1242
- list(opts?: {
1243
- prefix?: string;
1244
- limit?: number;
1245
- cursor?: string;
1246
- }): Promise<StorageListPage>;
1247
- /**
1248
- * Delete one or many assets (requires `storage.write`). Idempotent — deleting
1249
- * an absent key is a no-op success. `deleted` is the number of keys **accepted**
1250
- * for deletion (an absent key counts too), not a count of keys that existed.
1251
- */
1252
- delete(keyOrKeys: string | string[]): Promise<{
1253
- deleted: number;
1254
- }>;
1255
- /** Server-side copy within the app's silo (requires `storage.write`). */
1256
- copy(fromKey: string, toPathname: string, opts?: {
1257
- allowOverwrite?: boolean;
1258
- }): Promise<StoragePutResult>;
1259
- }
1260
- /**
1261
- * The data-plane context passed as the second argument to a function's default
1262
- * export. Mirrors the host-assembled `ctx` (`__buildCtx` in `runtime.rs`);
1263
- * every member is a host-provided async function bridged to a Rust backend.
1264
- */
1265
- interface OxyFunctionContext {
1266
- /** Invoking user (route) or system identity (schedule/airway). */
1267
- user: OxyFunctionUser;
1268
- /**
1269
- * The org's people directory. Requires `"org": { "read": true }` in this
1270
- * function's manifest entry — without it the call is rejected before any
1271
- * query reaches the database.
1272
- *
1273
- * For naming a person: an assignee, a roster entry, who submitted something.
1274
- * Returns a display name and a role, and deliberately **no email, no phone,
1275
- * no location**.
1276
- *
1277
- * Who is in it: **people who can reach this app**. Org members, plus frontline
1278
- * workers holding a grant on this app — `kind` tells them apart, so a caller
1279
- * that must not name a worker can refuse on the field rather than by
1280
- * convention. A worker's `role` is `null`: that vocabulary is org membership's
1281
- * and a worker has none.
1282
- *
1283
- * REQUIRED, like every sibling here — `oltp`, `secrets`, `email`, `storage`,
1284
- * `airway` are all gated and all declared required. The binding is
1285
- * unconditional: `__buildCtx` is a static string that attaches `org` whatever
1286
- * the manifest says, and the refusal lives in the op, not in the binding. An
1287
- * optional member would therefore be a lie in the other direction, and under
1288
- * `strict` it makes `ctx.org.people()` — the spelling in every doc here and
1289
- * the only one the host binds — fail with "possibly undefined".
1290
- */
1291
- org: {
1292
- people(): Promise<{
1293
- people: Array<{
1294
- id: string;
1295
- name: string;
1296
- /** The org role, or `null` for a frontline worker. */
1297
- role: string | null;
1298
- kind: "member" | "frontline";
1299
- }>;
1300
- total: number;
1301
- }>;
1302
- };
1303
- /** Read-only view of the app's configured secrets (project-scoped). */
1304
- env: Record<string, string>;
1305
- /** Structured per-invocation logging (captured + surfaced with the response). */
1306
- log(...args: unknown[]): void;
1307
- /** Read-only SQL (SELECT/WITH only), function-scoped row cap. Resolves to the rows. */
1308
- query(sql: string): Promise<OxyFunctionRow[]>;
1309
- /** Read-only SQL with a higher row cap, yielded to the caller in batches. */
1310
- queryStream(sql: string, opts?: {
1311
- batchSize?: number;
1312
- }): AsyncGenerator<OxyFunctionRow[], void, unknown>;
1313
- /**
1314
- * SSRF-allowlisted outbound HTTP with a response-size cap. Pass
1315
- * `{ encoding: "base64" }` for a binary response — the default UTF-8 decode
1316
- * corrupts it.
1317
- */
1318
- fetch(url: string, init?: OxyFetchInit): Promise<OxyFetchResult>;
1319
- warehouse: OxyWarehouseApi;
1320
- /**
1321
- * Run several statements atomically on one connection: commits when your
1322
- * callback resolves, rolls back when it throws, and rethrows your error
1323
- * either way. Resolves to whatever the callback returns.
1324
- *
1325
- * `database` must be in this function's manifest `destinations` — a
1326
- * transaction is a write, and the same fail-closed allowlist applies. Postgres
1327
- * only; other backends reject `ctx.tx` rather than faking it.
1328
- *
1329
- * **Do not catch a failed statement and return normally.** A statement the
1330
- * server rejects aborts the whole transaction, and `COMMIT` on an aborted
1331
- * transaction does not fail — Postgres applies nothing and reports success —
1332
- * so `ctx.tx` refuses to commit and throws instead, naming the statement that
1333
- * poisoned it. Let the error propagate.
1334
- *
1335
- * ```ts
1336
- * const orderId = await ctx.tx("appdb", async (tx) => {
1337
- * const [{ id }] = await tx.query(
1338
- * "INSERT INTO orders (table_no) VALUES ($1) RETURNING id",
1339
- * [tableNo],
1340
- * );
1341
- * for (const it of items) {
1342
- * await tx.exec(
1343
- * "INSERT INTO order_items (order_id, sku, qty) VALUES ($1, $2, $3)",
1344
- * [id, it.sku, it.qty],
1345
- * );
1346
- * }
1347
- * return id;
1348
- * });
1349
- * ```
1350
- */
1351
- tx<T>(database: string, fn: (tx: OxyTransaction) => Promise<T> | T): Promise<T>;
1352
- /**
1353
- * Read/write the app's OWN per-org OLTP schema (derived from its slug). The
1354
- * write half `ctx.warehouse` cannot give an app on a managed database. Gated
1355
- * by the fail-closed `oltp` manifest capability (`{ enabled: true }`). See
1356
- * {@link OxyOltpApi}.
1357
- */
1358
- oltp: OxyOltpApi;
1359
- secrets: OxySecretsApi;
1360
- semantic: OxySemanticApi;
1361
- airway: OxyAirwayApi;
1362
- email: OxyEmailApi;
1363
- storage: OxyStorageApi;
1364
- }
1365
- /** Signature of a function's default export: `export default async (req, ctx) => Response`. */
1366
- type OxyFunctionHandler = (req: OxyFunctionRequest, ctx: OxyFunctionContext) => Promise<Response> | Response;
1367
- //#endregion
1368
1175
  //#region src/custom-app/inject.d.ts
1369
1176
  /**
1370
1177
  * Shape of `window.__OXY_APP__` written by oxy at serve time.
@@ -1432,12 +1239,48 @@ declare function useMetricTree(opts?: UseMetricTreeOpts): MetricTreeHookResult<M
1432
1239
  * measure" question. Pass `null` to stay idle until a measure is chosen.
1433
1240
  */
1434
1241
  declare function useSensitivity(measureId: string | null, opts?: EndpointOpts): MetricTreeHookResult<SensitivityResult>;
1242
+ interface UsePredictOpts extends EndpointOpts, PredictOptions {}
1435
1243
  /**
1436
1244
  * Propagate hypothetical `(measure, delta)` changes upward through the
1437
1245
  * tree and return the estimated impact on every downstream measure — a
1438
1246
  * pure metric-tree walk, no warehouse query. Pass `null` to stay idle.
1247
+ *
1248
+ * Because it is database-free it can only use the coefficients it is GIVEN.
1249
+ * Without `opts.coefficients` from {@link useBaseline}, every driver edge
1250
+ * whose `.view.yml` declares no `coefficient:` contributes nothing and its
1251
+ * downstream measures are simply absent from `impacts` — no error, no
1252
+ * refusal. Without `opts.values`, multiplicative component edges come back
1253
+ * `unquantifiable` rather than sized.
1439
1254
  */
1440
- declare function usePredict(changes: PredictChange[] | null, opts?: EndpointOpts): MetricTreeHookResult<PredictResult>;
1255
+ declare function usePredict(changes: PredictChange[] | null, opts?: UsePredictOpts): MetricTreeHookResult<PredictResult>;
1256
+ /**
1257
+ * Value a scenario's starting point, and measure the coefficients it needs.
1258
+ *
1259
+ * Two warehouse reads: the current value of every node reachable from
1260
+ * `request.roots`, and — for driver edges declaring no `coefficient:` — a fit
1261
+ * over the window. Both are expensive, which is why they live here and not in
1262
+ * {@link usePredict}: predict is database-free by design so it can re-run per
1263
+ * keystroke, and it CANNOT measure a coefficient itself.
1264
+ *
1265
+ * That is the whole reason to call this. Feed `data.values` and `data.fitted`
1266
+ * into `usePredict`; omit them and an undeclared edge propagates nothing.
1267
+ * Pass `null` to stay idle until levers and a period are chosen.
1268
+ */
1269
+ declare function useBaseline(request: BaselineRequest | null, opts?: EndpointOpts): MetricTreeHookResult<BaselineResponse>;
1270
+ /**
1271
+ * Bucketed history for the levers and everything downstream, plus the forward
1272
+ * curve the forecaster expects next — the scenario's time axis.
1273
+ *
1274
+ * One warehouse query, so it belongs on a window change, not on a lever edit.
1275
+ * It returns the BASELINE curve only: the scenario's second curve is
1276
+ * arithmetic over this and a `usePredict` result — a proportional shift
1277
+ * landing `lag` buckets in — composed client-side precisely so editing a lever
1278
+ * costs no query.
1279
+ *
1280
+ * Treat a series with a `refusal` as a stated absence: it must not render as a
1281
+ * flat forward line. Pass `null` to stay idle.
1282
+ */
1283
+ declare function useProjection(request: ProjectionRequest | null, opts?: EndpointOpts): MetricTreeHookResult<ProjectionResponse>;
1441
1284
  /**
1442
1285
  * Period-over-period root-cause decomposition: recursively splits the
1443
1286
  * target measure by components and dimensions until the move concentrates.
@@ -1523,6 +1366,16 @@ interface WorldModel {
1523
1366
  interface WmInstance {
1524
1367
  key: string;
1525
1368
  display: string;
1369
+ /**
1370
+ * The org location this instance is, when the entity is bound to the
1371
+ * locations registry and the key is mapped; absent otherwise.
1372
+ */
1373
+ location?: {
1374
+ id: string;
1375
+ name: string;
1376
+ kind?: string | null;
1377
+ parent_id?: string | null;
1378
+ };
1526
1379
  }
1527
1380
  interface WmInstancesResponse {
1528
1381
  total: number;
@@ -1609,6 +1462,14 @@ interface UseWorldModelInstancesOpts {
1609
1462
  /** Max rows to return (default 50 server-side). */
1610
1463
  limit?: number;
1611
1464
  enabled?: boolean;
1465
+ /**
1466
+ * `"reach"` — only the instances whose place the viewer reaches, filtered
1467
+ * inside the scan so a page is a page of the right set. Needs the entity
1468
+ * bound to the org's locations registry (refused otherwise); an instance
1469
+ * whose key is unmapped is not in anyone's reach. The bundle's own app is
1470
+ * sent along so app-admin standing counts.
1471
+ */
1472
+ scope?: "reach";
1612
1473
  }
1613
1474
  interface UseWorldModelInstancesResult {
1614
1475
  data: WmInstancesResponse | null;
@@ -1732,5 +1593,5 @@ declare function createWorldModel(projectId: string | null, fetcher: AppFetcher)
1732
1593
  */
1733
1594
  declare function useWorldModel(): WorldModelApi;
1734
1595
  //#endregion
1735
- export { type AdditivityClass, type AgentArtifact, type AgentRunEvent, type AgentRunState, type AgentSqlArtifact, AnomaliesClient, type Anomaly, type AnomalyFilter, type AnomalySeverity, type AnomalyStatus, type AppFetcher, type BulkUpdateStatusResponse, type CustomAppDebugSnapshot, type CustomAppErrorReport, type DimensionOpportunity, type DistributionRequest, type DriverAttribution, type DriverConfidence, type DriverDirection, type DriverForm, type DriverStrength, type EdgeKind, type EmailAttachment, type EmailSendInput, type EmailSendResult, type ExpandedNode, type ExplainConfigOverride, type ExplainNode, type ExplainOptions, type ExplainOpts, type ExplainRequest, type ExplainResult, type ExplainSibling, type ExplainWarning, type FunctionError, type FunctionLog, type FunctionResult, type ListAnomaliesOptions, type ListAnomaliesResponse, type LoadManifestOptions, type MetricEdge, type MetricHandle, type MetricNode, type MetricScope, type MetricTree, MetricTreeClient, type MetricTreeHookResult, type OpportunityRequest, type OpportunityResult, type OxyAirwayApi, OxyAnswer, type OxyAnswerProps, OxyApiError, type OxyAppFunctionManifest, type OxyAppLogLevel, type OxyAppLogger, type OxyAppManifest, type OxyAppPerformanceManifest, OxyAppProvider, type OxyAppProviderProps, OxyChat, type OxyChatProps, type OxyEmailApi, type OxyFetchResult, type OxyFunctionContext, type OxyFunctionHandler, type OxyFunctionRequest, type OxyFunctionRow, type OxyFunctionUser, type OxyIdentityKind, type OxyInjectedAppConfig, type OxyOltpApi, type OxyOrgTeam, type OxySecretsApi, type OxySemanticApi, type OxyStorageApi, type OxyTransaction, type OxyWarehouseApi, type PredictChange, type PredictImpact, type PredictResult, type ProcedureProgress, type ProcedureResult, type ProcedureRunState, type ResolvedCustomAppManifest, type ScanFailure, type ScanOptions, type ScanResponse, type SegmentOpportunity, type SemanticArrayOp, type SemanticDateRangeOp, type SemanticFilter, type SemanticScalarOp, type SemanticTimeDimension, type SensitivityDriver, type SensitivityResult, type SizeOpts, type SkippedDimension, type SplitKind, type StorageDownloadUrl, type StorageListPage, type StorageObject, type StoragePutOptions, type StoragePutResult, type StorageUploadUrl, type StorageUploadUrlInput, type TimeDimensionsResponse, type UseAgentRunInput, type UseAgentRunResult, type UseFunctionResult, type UseMeasureBreakdownResult, type UseMetricTreeOpts, type UseProcedureRunInput, type UseProcedureRunOpts, type UseProcedureRunResult, type UseQueryInput, type UseQueryOpts, type UseQueryResult, type UseSemanticQueryInput, type UseSemanticQueryOpts, type UseSemanticQueryResult, type UseWorldModelGraphResult, type UseWorldModelInstancesOpts, type UseWorldModelInstancesResult, type WmBreakdownEdge, type WmBreakdownNode, type WmEntityCount, type WmFilterCountsResponse, type WmInstance, type WmInstancesResponse, type WmMeasureBreakdown, type WmMeasureBreakdownEvent, type WorldModel, type WorldModelApi, type WorldModelDimension, type WorldModelEdge, type WorldModelEntity, type WorldModelInducedMeasure, type WorldModelMeasure, WorldModelScopeUnsupportedError, _resetCustomAppManifestCacheForTest, apiErrorFromResponse, base64ToBytes, bytesToBase64, createWorldModel, getCustomAppDebug, getOxyAppLogger, interpretCustomAppError, loadCustomAppManifest, readInjectedAppConfig, readJsonSseStream, setOxyAppLogger, useAgentRun, useDistribution, useExplain, useFunction, useMeasureBreakdown, useMetricTree, useOpportunity, useOxyApp, usePredict, useProcedureRun, useQuery, useResolvedManifest, useSemanticQuery, useSensitivity, useTimeDimensions, useTrackEvent, useWorldModel, useWorldModelGraph, useWorldModelInstances };
1596
+ export { type AdditivityClass, type AgentArtifact, type AgentRunEvent, type AgentRunState, type AgentSqlArtifact, AnomaliesClient, type Anomaly, type AnomalyFilter, type AnomalySeverity, type AnomalyStatus, type AppFetcher, type BaselineInstance, type BaselineRequest, type BaselineResponse, type BenchmarkStatistic, type BulkUpdateStatusResponse, type CohortRequest, type CohortSubject, type CustomAppDebugSnapshot, type CustomAppErrorReport, type DimensionOpportunity, type DistributionRequest, type DriverAttribution, type DriverConfidence, type DriverDirection, type DriverForm, type DriverStrength, type EdgeKind, type EmailAttachment, type EmailSendInput, type EmailSendResult, type ExcludedSubject, type ExpandedNode, type ExplainConfigOverride, type ExplainNode, type ExplainOptions, type ExplainOpts, type ExplainRequest, type ExplainResult, type ExplainSibling, type ExplainWarning, type FittedDriver, type ForecastPoint, type FunctionError, type FunctionLog, type FunctionResult, type HistoryPoint, type ListAnomaliesOptions, type ListAnomaliesResponse, type LoadManifestOptions, type MeasureProjection, type MeasureValues, type MetricEdge, type MetricHandle, type MetricNode, type MetricScope, type MetricTree, MetricTreeClient, type MetricTreeHookResult, type OpportunityRequest, type OpportunityResult, type OxyAirwayApi, OxyAnswer, type OxyAnswerProps, OxyApiError, type OxyAppFunctionManifest, type OxyAppLogLevel, type OxyAppLogger, type OxyAppManifest, type OxyAppPerformanceManifest, OxyAppProvider, type OxyAppProviderProps, OxyChat, type OxyChatProps, type OxyEmailApi, type OxyFetchResult, type OxyFunctionContext, type OxyFunctionHandler, type OxyFunctionRequest, type OxyFunctionRow, type OxyFunctionUser, type OxyIdentityKind, type OxyInjectedAppConfig, type OxyOltpApi, type OxyOrgAssignment, type OxyOrgPlace, type OxyOrgTeam, type OxyReach, type OxySecretsApi, type OxySemanticApi, type OxyStorageApi, type OxyTransaction, type OxyWarehouseApi, PeerCohortClient, type PeerCohortResult, type PredictChange, type PredictImpact, type PredictOptions, type PredictResult, type ProcedureProgress, type ProcedureResult, type ProcedureRunState, type ProjectionGranularity, type ProjectionRequest, type ProjectionResponse, type ResolvedCustomAppManifest, type ScanFailure, type ScanOptions, type ScanResponse, type SegmentOpportunity, type SemanticArrayOp, type SemanticDateRangeOp, type SemanticFilter, type SemanticScalarOp, type SemanticTimeDimension, type SensitivityDriver, type SensitivityResult, type SizeOpts, type SkippedDimension, type SplitKind, type StorageDownloadUrl, type StorageListPage, type StorageObject, type StoragePutOptions, type StoragePutResult, type StorageUploadUrl, type StorageUploadUrlInput, type TimeDimensionsResponse, type UnvaluedNode, type UseAgentRunInput, type UseAgentRunResult, type UseFunctionResult, type UseMeasureBreakdownResult, type UseMetricTreeOpts, type UsePredictOpts, type UseProcedureRunInput, type UseProcedureRunOpts, type UseProcedureRunResult, type UseQueryInput, type UseQueryOpts, type UseQueryResult, type UseSemanticQueryInput, type UseSemanticQueryOpts, type UseSemanticQueryResult, type UseWorldModelGraphResult, type UseWorldModelInstancesOpts, type UseWorldModelInstancesResult, type WmBreakdownEdge, type WmBreakdownNode, type WmEntityCount, type WmFilterCountsResponse, type WmInstance, type WmInstancesResponse, type WmMeasureBreakdown, type WmMeasureBreakdownEvent, type WorldModel, type WorldModelApi, type WorldModelDimension, type WorldModelEdge, type WorldModelEntity, type WorldModelInducedMeasure, type WorldModelMeasure, WorldModelScopeUnsupportedError, _resetCustomAppManifestCacheForTest, apiErrorFromResponse, base64ToBytes, bytesToBase64, createWorldModel, getCustomAppDebug, getOxyAppLogger, interpretCustomAppError, loadCustomAppManifest, readInjectedAppConfig, readJsonSseStream, setOxyAppLogger, useAgentRun, useBaseline, useDistribution, useExplain, useFunction, useMeasureBreakdown, useMetricTree, useOpportunity, useOxyApp, usePredict, useProcedureRun, useProjection, useQuery, useResolvedManifest, useSemanticQuery, useSensitivity, useTimeDimensions, useTrackEvent, useWorldModel, useWorldModelGraph, useWorldModelInstances };
1736
1597
  //# sourceMappingURL=index.d.mts.map