@stigg/agent-sdk-js 0.0.0-stage → 0.1.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.
- package/README.md +1296 -2
- package/dist/bindings/client/core/registry.d.ts +12 -0
- package/dist/bindings/client/core/registry.js +22 -0
- package/dist/bindings/client/core/types.d.ts +117 -0
- package/dist/bindings/client/core/types.js +13 -0
- package/dist/bindings/client/core/wrap-client.d.ts +6 -0
- package/dist/bindings/client/core/wrap-client.js +258 -0
- package/dist/bindings/client/gateways/bedrock/bedrock-recipe.d.ts +17 -0
- package/dist/bindings/client/gateways/bedrock/bedrock-recipe.js +134 -0
- package/dist/bindings/client/gateways/bedrock/types.d.ts +13 -0
- package/dist/bindings/client/gateways/bedrock/types.js +2 -0
- package/dist/bindings/client/gateways/litellm/litellm-recipe.d.ts +17 -0
- package/dist/bindings/client/gateways/litellm/litellm-recipe.js +171 -0
- package/dist/bindings/client/gateways/litellm/types.d.ts +30 -0
- package/dist/bindings/client/gateways/litellm/types.js +2 -0
- package/dist/bindings/client/gateways/openrouter/openrouter-recipe.d.ts +20 -0
- package/dist/bindings/client/gateways/openrouter/openrouter-recipe.js +244 -0
- package/dist/bindings/client/gateways/openrouter/sdk-usage.d.ts +37 -0
- package/dist/bindings/client/gateways/openrouter/sdk-usage.js +63 -0
- package/dist/bindings/client/gateways/openrouter/types.d.ts +78 -0
- package/dist/bindings/client/gateways/openrouter/types.js +2 -0
- package/dist/bindings/client/providers/anthropic/anthropic-recipe.d.ts +34 -0
- package/dist/bindings/client/providers/anthropic/anthropic-recipe.js +169 -0
- package/dist/bindings/client/providers/gemini/gemini-recipe.d.ts +54 -0
- package/dist/bindings/client/providers/gemini/gemini-recipe.js +243 -0
- package/dist/bindings/client/providers/groq/groq-recipe.d.ts +20 -0
- package/dist/bindings/client/providers/groq/groq-recipe.js +101 -0
- package/dist/bindings/client/providers/mistral/mistral-recipe.d.ts +22 -0
- package/dist/bindings/client/providers/mistral/mistral-recipe.js +114 -0
- package/dist/bindings/client/providers/mistral/types.d.ts +38 -0
- package/dist/bindings/client/providers/mistral/types.js +2 -0
- package/dist/bindings/client/providers/ollama/ollama-recipe.d.ts +31 -0
- package/dist/bindings/client/providers/ollama/ollama-recipe.js +107 -0
- package/dist/bindings/client/providers/ollama/types.d.ts +24 -0
- package/dist/bindings/client/providers/ollama/types.js +2 -0
- package/dist/bindings/client/providers/openai/openai-recipe.d.ts +71 -0
- package/dist/bindings/client/providers/openai/openai-recipe.js +122 -0
- package/dist/bindings/client/providers/typesafe/typesafe-recipe.d.ts +44 -0
- package/dist/bindings/client/providers/typesafe/typesafe-recipe.js +83 -0
- package/dist/bindings/proxy/headers.d.ts +6 -0
- package/dist/bindings/proxy/headers.js +12 -0
- package/dist/bindings/proxy/index.d.ts +21 -0
- package/dist/bindings/proxy/index.js +18 -0
- package/dist/bindings/proxy/types.d.ts +8 -0
- package/dist/bindings/proxy/types.js +2 -0
- package/dist/bindings/shared/estimate.d.ts +27 -0
- package/dist/bindings/shared/estimate.js +38 -0
- package/dist/bindings/shared/meter-stream.d.ts +18 -0
- package/dist/bindings/shared/meter-stream.js +151 -0
- package/dist/bindings/shared/token-usage.d.ts +240 -0
- package/dist/bindings/shared/token-usage.js +574 -0
- package/dist/bindings/shared/types.d.ts +129 -0
- package/dist/bindings/shared/types.js +2 -0
- package/dist/bindings/shared/weighted-metrics.d.ts +37 -0
- package/dist/bindings/shared/weighted-metrics.js +47 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.js +99 -0
- package/dist/core/context.d.ts +160 -0
- package/dist/core/context.js +221 -0
- package/dist/core/dimensions.d.ts +22 -0
- package/dist/core/dimensions.js +47 -0
- package/dist/core/hooks.d.ts +21 -0
- package/dist/core/hooks.js +47 -0
- package/dist/core/session.d.ts +190 -0
- package/dist/core/session.js +448 -0
- package/dist/decision-text.d.ts +15 -0
- package/dist/decision-text.js +54 -0
- package/dist/engine/composite-engine.d.ts +72 -0
- package/dist/engine/composite-engine.js +135 -0
- package/dist/engine/create-engine.d.ts +16 -0
- package/dist/engine/create-engine.js +93 -0
- package/dist/engine/decision.d.ts +42 -0
- package/dist/engine/decision.js +135 -0
- package/dist/engine/engine.d.ts +166 -0
- package/dist/engine/engine.js +11 -0
- package/dist/engine/node-sdk-engine.d.ts +125 -0
- package/dist/engine/node-sdk-engine.js +262 -0
- package/dist/engine/report.d.ts +106 -0
- package/dist/engine/report.js +188 -0
- package/dist/engine/rest-governance-engine.d.ts +47 -0
- package/dist/engine/rest-governance-engine.js +220 -0
- package/dist/errors.d.ts +86 -0
- package/dist/errors.js +161 -0
- package/dist/headers.d.ts +40 -0
- package/dist/headers.js +82 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.js +106 -0
- package/dist/internal.d.ts +33 -0
- package/dist/internal.js +87 -0
- package/dist/logger.d.ts +15 -0
- package/dist/logger.js +20 -0
- package/dist/sdk-identity.d.ts +36 -0
- package/dist/sdk-identity.js +43 -0
- package/dist/stigg-agent-sdk.d.ts +300 -0
- package/dist/stigg-agent-sdk.js +656 -0
- package/dist/types.d.ts +642 -0
- package/dist/types.js +147 -0
- package/package.json +89 -3
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vocabulary shared by the core, the engines and every binding, and the argument list of
|
|
3
|
+
* every call on the public surface.
|
|
4
|
+
*
|
|
5
|
+
* Terms follow Stigg governance rather than the draft design notes: provision/archive (not
|
|
6
|
+
* register/de-register), ingest (not report), assignment (not budget), dimensions (not tags).
|
|
7
|
+
*
|
|
8
|
+
* The imports below are all `import type`, so they are erased at compile time: this file pulls in
|
|
9
|
+
* no module at runtime and stays the leaf everything else can depend on.
|
|
10
|
+
*/
|
|
11
|
+
import { EngineError } from './errors';
|
|
12
|
+
import type { StiggContext } from './core/context';
|
|
13
|
+
import type { SessionSummary, SessionHooks } from './core/session';
|
|
14
|
+
import type { GovernanceHeaders } from './headers';
|
|
15
|
+
import type { ClientRecipe } from './bindings/client/core/types';
|
|
16
|
+
import type { ObservedVendor } from './bindings/shared/token-usage';
|
|
17
|
+
import type { EventIngest } from './engine/engine';
|
|
18
|
+
/**
|
|
19
|
+
* What a budget is measured against. Exactly one of the two - the same mutual exclusion the
|
|
20
|
+
* Stigg check endpoint enforces, expressed so the compiler catches it instead of the API.
|
|
21
|
+
*/
|
|
22
|
+
export type Entitlement = {
|
|
23
|
+
currencyId: string;
|
|
24
|
+
featureId?: never;
|
|
25
|
+
} | {
|
|
26
|
+
featureId: string;
|
|
27
|
+
currencyId?: never;
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Which of Stigg's two ingest APIs a run reports through.
|
|
31
|
+
*
|
|
32
|
+
* `'sync'` is `usage.report`/`reportUsageBulk`: a fixed DTO of `featureId` and a whole-number
|
|
33
|
+
* `value` per metric, aggregated the moment it lands, so the next check sees it. It costs more
|
|
34
|
+
* latency and has the lower rate limit.
|
|
35
|
+
*
|
|
36
|
+
* `'async'` is `events.report`/`reportEvent`: the call is reported as one event and Stigg's own
|
|
37
|
+
* meters turn it into usage, seconds later. Low latency, a far higher rate limit, and nothing
|
|
38
|
+
* meters at all until a **raw-events meter** is configured for the event name on Stigg's side.
|
|
39
|
+
*/
|
|
40
|
+
export type IngestMode = 'sync' | 'async';
|
|
41
|
+
/** The id to send as `featureId`/`currencyId`, whichever kind the entitlement is. */
|
|
42
|
+
export declare const entitlementId: (entitlement: Entitlement) => string;
|
|
43
|
+
/** 'credit' targets read the customer's credit pool; 'feature' targets need an entitling subscription. */
|
|
44
|
+
export declare const entitlementKind: (entitlement: Entitlement) => "credit" | "feature";
|
|
45
|
+
/**
|
|
46
|
+
* The entitlement as every Stigg endpoint takes it: one of two mutually exclusive id fields,
|
|
47
|
+
* ready to spread.
|
|
48
|
+
*
|
|
49
|
+
* ```ts
|
|
50
|
+
* await client.v1Beta.customers.entitlements.check(customerId, { ...entitlementRef(entitlement), requestedUsage });
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Narrowing on the property rather than through {@link entitlementKind} is the point: a function
|
|
54
|
+
* call cannot narrow a union, so every hand-written version of this needed a `!` on each branch -
|
|
55
|
+
* an assertion that the union already guarantees and that would survive the union changing.
|
|
56
|
+
*/
|
|
57
|
+
export declare const entitlementRef: (entitlement: Entitlement) => {
|
|
58
|
+
currencyId: string;
|
|
59
|
+
} | {
|
|
60
|
+
featureId: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Why an entitlement cannot be billed against, if one was named at all.
|
|
64
|
+
*
|
|
65
|
+
* Naming none is legal: it is how a caller asks for metering without enforcement, so undefined is
|
|
66
|
+
* not an error here. The one place an entitlement is genuinely required is an assignment, which is
|
|
67
|
+
* written over `(entity x entitlement)` - and `register` refuses that case itself, because only it
|
|
68
|
+
* knows the agent to name in the message.
|
|
69
|
+
*
|
|
70
|
+
* What stays refused is an entitlement that is named and names nothing. The union permits
|
|
71
|
+
* `{ currencyId: '' }`, and an empty currency is falsy - so it classifies as a feature and every
|
|
72
|
+
* branch downstream then asks for a `featureId` that is not there. An empty id is a typo rather
|
|
73
|
+
* than an intention, and letting it through meters a run against nothing while it still looks
|
|
74
|
+
* enforced.
|
|
75
|
+
*/
|
|
76
|
+
export declare function entitlementError(entitlement: Entitlement | undefined): Error | undefined;
|
|
77
|
+
/**
|
|
78
|
+
* A provider-neutral token measurement. Every client/orchestrator binding normalizes into this
|
|
79
|
+
* before anything touches Stigg, so the mapper below is written once for all of them.
|
|
80
|
+
*/
|
|
81
|
+
export type TokenUsage = {
|
|
82
|
+
input: number;
|
|
83
|
+
output: number;
|
|
84
|
+
cacheRead: number;
|
|
85
|
+
cacheCreate: number;
|
|
86
|
+
/**
|
|
87
|
+
* What the vendor says the call cost in total, when it says anything.
|
|
88
|
+
*
|
|
89
|
+
* Apart from the four above, and deliberately so: those are disjoint and add up to the call,
|
|
90
|
+
* while this one *overlaps all of them*. It is here because a mapper that wants one number
|
|
91
|
+
* should be able to use the vendor's own rather than re-adding the parts and hoping the
|
|
92
|
+
* arithmetic matches - the two can differ, and when they do the vendor's is the one that gets
|
|
93
|
+
* invoiced.
|
|
94
|
+
*
|
|
95
|
+
* Read from the response where the vendor publishes a total, and summed from the four where it
|
|
96
|
+
* publishes none and they are disjoint. Undefined where neither is true, which is the honest
|
|
97
|
+
* answer rather than a guess: see `COMPUTED_TOTALS`.
|
|
98
|
+
*/
|
|
99
|
+
total?: number;
|
|
100
|
+
};
|
|
101
|
+
export declare const emptyTokenUsage: () => TokenUsage;
|
|
102
|
+
/**
|
|
103
|
+
* What one completed call reports: feature id -> whole-number value. One report each.
|
|
104
|
+
*
|
|
105
|
+
* A record rather than a list of `{featureId, value}` pairs, so the same feature cannot appear
|
|
106
|
+
* twice - two reports of one feature in one call would collide on idempotency keys and silently
|
|
107
|
+
* lose one of them.
|
|
108
|
+
*/
|
|
109
|
+
export type Metrics = Record<string, number>;
|
|
110
|
+
/**
|
|
111
|
+
* Turns what a binding observed into the metrics to report.
|
|
112
|
+
*
|
|
113
|
+
* Defaults to the vendor's own: whatever names and values came back on the response. Supply your
|
|
114
|
+
* own to report something else - see `unitsPerTokens` for the one-number case, and each client
|
|
115
|
+
* recipe for what that vendor meters by default.
|
|
116
|
+
*/
|
|
117
|
+
export type MetricsMapper = (measurement: Measurement) => Metrics;
|
|
118
|
+
/**
|
|
119
|
+
* What one completed call is worth, as the binding that observed it saw it.
|
|
120
|
+
*
|
|
121
|
+
* Two views of one call, and both are useful. `metrics` is what the vendor said, in the vendor's
|
|
122
|
+
* own words - reported as-is, so what lands in Stigg reconciles against what the vendor bills.
|
|
123
|
+
* `usage` is the same call flattened into four comparable counters, which is what a mapper writes
|
|
124
|
+
* against when it wants one number or one vocabulary across vendors.
|
|
125
|
+
*/
|
|
126
|
+
export type Measurement = {
|
|
127
|
+
/** The vendor's own field names and values, verbatim. What gets reported by default. */
|
|
128
|
+
metrics?: Metrics;
|
|
129
|
+
/** The same call, normalized and non-overlapping. Context for writing your own mapper. */
|
|
130
|
+
usage?: TokenUsage;
|
|
131
|
+
/**
|
|
132
|
+
* The vendor's usage object as it arrived, before any counter was picked out of it.
|
|
133
|
+
*
|
|
134
|
+
* Carried only so an event can state what the vendor said in full - it rides the event's
|
|
135
|
+
* dimensions under a `raw.` prefix, flattened to scalars, and a meter can then aggregate a
|
|
136
|
+
* counter this SDK does not know about yet. Never reported as a metric: a feature id has to be
|
|
137
|
+
* something the customer's catalog knows, and a vendor's changelog must not be able to invent
|
|
138
|
+
* one. The usage block only - never a response body, so no prompt or completion text leaves
|
|
139
|
+
* the process.
|
|
140
|
+
*/
|
|
141
|
+
raw?: Record<string, unknown>;
|
|
142
|
+
/** Per-call attribution, merged over the run's own dimensions. e.g. the model that served. */
|
|
143
|
+
dimensions?: Record<string, string>;
|
|
144
|
+
/**
|
|
145
|
+
* Set when a counter here is this SDK's estimate rather than something the vendor stated.
|
|
146
|
+
*
|
|
147
|
+
* Only one thing sets it today: a stream the consumer abandoned. Anthropic states the output
|
|
148
|
+
* count on the event that ends the stream, so a reply stopped halfway never states one - and
|
|
149
|
+
* reporting nothing for text the customer read, and the vendor billed, is a hole rather than a
|
|
150
|
+
* conservative answer.
|
|
151
|
+
*
|
|
152
|
+
* Carried on the measurement rather than folded into a dimension so it reaches the run's
|
|
153
|
+
* `usageMapper`, which is what makes it a decision rather than a surprise: a mapper that would
|
|
154
|
+
* rather bill nothing than bill an estimate can branch on this and return `{}`.
|
|
155
|
+
*/
|
|
156
|
+
estimated?: true;
|
|
157
|
+
/** The vendor's own request id, when it has one that is stable across its retries. */
|
|
158
|
+
idempotencyKey?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Why this measurement does not cover everything the vendor billed, when it does not.
|
|
161
|
+
*
|
|
162
|
+
* For the case where usage exists that the response never carried - not a disagreement between
|
|
163
|
+
* two numbers the vendor gave, which is data the caller can judge for themselves, but work
|
|
164
|
+
* that was charged for and never reported. What is here is still ingested: a short measurement
|
|
165
|
+
* beats none. The reason is then raised through the same channel a failed read uses, so
|
|
166
|
+
* `onError` fires and `summary().ingestFailures` counts it, rather than the shortfall being
|
|
167
|
+
* something a reader of the docs was supposed to have known about.
|
|
168
|
+
*/
|
|
169
|
+
incomplete?: string;
|
|
170
|
+
};
|
|
171
|
+
/** What a run reports when nobody says otherwise: exactly what the vendor stated. */
|
|
172
|
+
export declare const vendorMetrics: MetricsMapper;
|
|
173
|
+
/** A binding may report either shape; `TokenUsage` is the common case and stays terse. */
|
|
174
|
+
export declare const toMeasurement: (value: TokenUsage | Measurement) => Measurement;
|
|
175
|
+
/**
|
|
176
|
+
* The amount to report when this run is attributed to a governance entity.
|
|
177
|
+
*
|
|
178
|
+
* Governance counts in whole units and cannot be made to count otherwise from here: its ingest
|
|
179
|
+
* schema is `z.number().int()`, and the counter behind it is a Redis `HINCRBY`, which takes an
|
|
180
|
+
* integer delta. A fractional amount is rejected at that boundary - the wallet still moves, the
|
|
181
|
+
* per-entity counter does not, and nothing in the response says so. An agent's budget silently
|
|
182
|
+
* stops advancing while its spend carries on.
|
|
183
|
+
*
|
|
184
|
+
* So an attributed run rounds up, and only an attributed run: a run that names no entity reports
|
|
185
|
+
* what its mapper produced, because the wallet takes fractions and nothing downstream of it
|
|
186
|
+
* counts in whole units.
|
|
187
|
+
*
|
|
188
|
+
* Rounding *up* rather than to nearest, because a call that happened must cost something: at any
|
|
189
|
+
* sane denomination most calls are worth less than one unit, and rounding to nearest would bill
|
|
190
|
+
* nothing for nearly all of them.
|
|
191
|
+
*
|
|
192
|
+
* TODO: delete this, and the call sites that use it, once governance counts fractional usage.
|
|
193
|
+
* The platform fix is to scale internally - store micro-units and accept fractions at the API
|
|
194
|
+
* boundary - which keeps `HINCRBY` working and is the only way an agent budget can be
|
|
195
|
+
* denominated finely enough to price a call worth a tenth of a cent. Until then this is a
|
|
196
|
+
* deliberate overcharge on small calls, which is why it is announced rather than silent.
|
|
197
|
+
*/
|
|
198
|
+
export declare const wholeUnitsForGovernance: (value: number) => number;
|
|
199
|
+
/**
|
|
200
|
+
* Why a measured value cannot be reported, if it cannot.
|
|
201
|
+
*
|
|
202
|
+
* Zero is not an error - a cached refusal or an empty stream is a normal turn - but a mapper
|
|
203
|
+
* dividing by an unset config returns NaN or Infinity, and reading those as "nothing was used"
|
|
204
|
+
* meters the run at zero while every call still reports success. Those are refused everywhere.
|
|
205
|
+
*
|
|
206
|
+
* A fraction is refused on one path only, and the rule is the endpoint's rather than this SDK's:
|
|
207
|
+
*
|
|
208
|
+
* - **The wallet** takes them. `credits.consumption.consume` and its async twin accept a
|
|
209
|
+
* fractional `amount`, and balances are fractional too - which they have to be, or a currency
|
|
210
|
+
* could never be denominated finely enough to price a call worth a tenth of a cent.
|
|
211
|
+
* - **An event** takes them. The number rides as a dimension and a meter sums what it finds,
|
|
212
|
+
* which is the only way a gateway's `cost` reports honestly.
|
|
213
|
+
* - **The usage report** does not: `POST /api/v1/usage` answers
|
|
214
|
+
* `400 Expected integer, received float`. That is the one case refused here, where the caller
|
|
215
|
+
* can still see which metric it was - metering failures are swallowed into `onError`, so a 400
|
|
216
|
+
* three frames down is a silent gap rather than an error anyone reads.
|
|
217
|
+
*
|
|
218
|
+
* Shared, because every path that reports usage has to answer this the same way.
|
|
219
|
+
*/
|
|
220
|
+
export declare function usageValueError(value: number, mode?: IngestMode, drawsWallet?: boolean): EngineError | undefined;
|
|
221
|
+
/**
|
|
222
|
+
* One metric, summed from every token class and divided by the unit size.
|
|
223
|
+
*
|
|
224
|
+
* The mapper for an application that prices tokens itself. Named for what it produces - units
|
|
225
|
+
* under an id - rather than for credits, because it does not deduct any: it reports a number, and
|
|
226
|
+
* that number draws the wallet only when the id happens to name a credit currency. Which it is
|
|
227
|
+
* decides the whole ingest path, so a mapper that claimed to know was the wrong place to look.
|
|
228
|
+
*
|
|
229
|
+
* **Prefer reporting the raw counters and converting in Stigg.** A consumption mapping on the plan
|
|
230
|
+
* does the same arithmetic where a pricing change does not need a deploy, and it is the only place
|
|
231
|
+
* that can weight cache reads cheaper. Reach for this when the application deliberately prices
|
|
232
|
+
* itself.
|
|
233
|
+
*
|
|
234
|
+
* The division is reported as it comes out. A credit currency takes fractions, so a call worth
|
|
235
|
+
* four fifths of a unit costs four fifths of one - rounding it up here would be this SDK's
|
|
236
|
+
* arithmetic on someone else's price. A **feature** is the exception: `POST /api/v1/usage` takes
|
|
237
|
+
* integers, so pick a `tokensPerUnit` that divides, or round in your own mapper.
|
|
238
|
+
*
|
|
239
|
+
* @param id The Stigg feature id or credit currency id to report under.
|
|
240
|
+
* @param tokensPerUnit How many tokens make one unit.
|
|
241
|
+
*/
|
|
242
|
+
export declare const unitsPerTokens: (id: string, tokensPerUnit?: number) => MetricsMapper;
|
|
243
|
+
/** What the caller should do about a denial. A refusal is a commercial event, not an error. */
|
|
244
|
+
export type Remedy = 'top-up' | 'raise-budget' | 'subscribe' | 'retry';
|
|
245
|
+
/**
|
|
246
|
+
* Which client answered, or - for 'cache', 'policy' and 'unenforced' - that nothing was asked.
|
|
247
|
+
*
|
|
248
|
+
* `'unenforced'` is the one that could not have been asked: the run named no entitlement, so there
|
|
249
|
+
* is no budget to consult and every decision it hands back is an allow.
|
|
250
|
+
*/
|
|
251
|
+
export type DecisionSource = 'rest' | 'node-sdk' | 'cache' | 'policy' | 'unenforced';
|
|
252
|
+
/**
|
|
253
|
+
* The structured refusal. Returned, never thrown, everywhere the caller has a return path:
|
|
254
|
+
* the denial path is a revenue surface (top-up, upgrade), so it must be inspectable.
|
|
255
|
+
*/
|
|
256
|
+
export type AccessDecision = {
|
|
257
|
+
hasAccess: boolean;
|
|
258
|
+
/** Which entity blocked - a governance node, or the customer when governance is not in play. */
|
|
259
|
+
deniedBy?: string;
|
|
260
|
+
/**
|
|
261
|
+
* Why it was refused. Stigg's `accessDeniedReason` verbatim, except where `locallyDerived` says
|
|
262
|
+
* the SDK reached the verdict itself. `InsufficientCredits` and `BudgetExceeded` differ.
|
|
263
|
+
*/
|
|
264
|
+
reason?: string;
|
|
265
|
+
remedy?: Remedy;
|
|
266
|
+
/** Tightest remaining budget across the pool and the chain. Undefined when nothing is bounded. */
|
|
267
|
+
remaining?: number;
|
|
268
|
+
usageLimit?: number | null;
|
|
269
|
+
currentUsage?: number;
|
|
270
|
+
source: DecisionSource;
|
|
271
|
+
checkedAt: number;
|
|
272
|
+
/** True when the engine served this from its own fallback rather than live state. */
|
|
273
|
+
isFallback?: boolean;
|
|
274
|
+
/**
|
|
275
|
+
* True when the SDK decided this rather than Stigg. A credit check reads the balance and
|
|
276
|
+
* compares it against the requested amount here, because the credit read takes no requested
|
|
277
|
+
* usage to adjudicate - so the balance is Stigg's, and the refusal is the SDK's.
|
|
278
|
+
*
|
|
279
|
+
* Distinct from `isFallback`: that one says the state was stale, this one says the state was
|
|
280
|
+
* live and the arithmetic was ours. Both can be true, and `strictCheck: false` on the engine's
|
|
281
|
+
* capabilities is the same admission at the level above.
|
|
282
|
+
*/
|
|
283
|
+
locallyDerived?: boolean;
|
|
284
|
+
/** One line for a log: every field that matters, already formatted. */
|
|
285
|
+
summary: string;
|
|
286
|
+
/** What to tell the user. A refusal is a commercial event, so it carries its own remedy. */
|
|
287
|
+
message: string;
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* The outcome of one ingest - which is now one report per metric, so the result is per metric too.
|
|
291
|
+
* Never a rejection: metering must not break the caller's run.
|
|
292
|
+
*/
|
|
293
|
+
export type IngestResult = {
|
|
294
|
+
/** True only when every metric landed. One failure among four is not a success. */
|
|
295
|
+
ok: boolean;
|
|
296
|
+
/** What was reported, by feature id. Empty when the call measured nothing at all. */
|
|
297
|
+
reported: Metrics;
|
|
298
|
+
/**
|
|
299
|
+
* The key the report deduped on. Under `'sync'` each metric is keyed
|
|
300
|
+
* `${idempotencyKey}:${featureId}`, so they dedupe independently; under `'async'` the whole
|
|
301
|
+
* turn is one event under this key alone.
|
|
302
|
+
*/
|
|
303
|
+
idempotencyKey: string;
|
|
304
|
+
/** Which API this went through, because the two answer with different amounts of truth. */
|
|
305
|
+
mode: IngestMode;
|
|
306
|
+
/**
|
|
307
|
+
* Stigg reported nothing consumed for every metric: these keys were already ingested.
|
|
308
|
+
* **Undefined under `'async'`** - the event API answers with an empty body, so a replay is
|
|
309
|
+
* something Stigg does and the SDK cannot observe. Saying `false` would be a claim, not a fact.
|
|
310
|
+
*/
|
|
311
|
+
replayed?: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Credits this report actually drew, when Stigg said so. Absent when the features reported are
|
|
314
|
+
* not credit-backed - which is also when the run's budget will never drain - and always absent
|
|
315
|
+
* under `'async'`, which returns no balance at all.
|
|
316
|
+
*/
|
|
317
|
+
creditsConsumed?: number;
|
|
318
|
+
error?: unknown;
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* What a check resolves to when the engine could not answer it. A policy, not a notification:
|
|
322
|
+
* hooks are told what happened and cannot change it, while this decides the outcome. Ingest has
|
|
323
|
+
* no equivalent - a failed ingest never affects a run, it only reaches `hooks.onError`.
|
|
324
|
+
*/
|
|
325
|
+
export type CheckErrorAction = 'deny' | 'allow';
|
|
326
|
+
/** What binding a vendor client needs: how to recognise it, what to watch, and what governs it. */
|
|
327
|
+
export type ClientOptions = {
|
|
328
|
+
/**
|
|
329
|
+
* Use this adapter instead of matching the client by its shape and base URL. The built-in ones
|
|
330
|
+
* cover Anthropic, OpenAI, OpenRouter and LiteLLM; reach for this when a vendor none of them
|
|
331
|
+
* identifies needs one of its own.
|
|
332
|
+
*/
|
|
333
|
+
recipe?: ClientRecipe;
|
|
334
|
+
/** Everything this client reports back while it works. See {@link SessionHooks}. */
|
|
335
|
+
hooks?: SessionHooks;
|
|
336
|
+
/** The default context for every call through this client. Any call may override it. */
|
|
337
|
+
stiggContext?: StiggContext;
|
|
338
|
+
};
|
|
339
|
+
/**
|
|
340
|
+
* `register` - giving an agent an identity, a budget, and a place in the tree.
|
|
341
|
+
*
|
|
342
|
+
* A write, which is why it is its own call rather than something a gated call carries: a limit is
|
|
343
|
+
* not a field on the entity, so this is two idempotent upserts underneath - the entity, then an
|
|
344
|
+
* assignment over `(entity x entitlement)`. That is also why `parentId` belongs here and not on
|
|
345
|
+
* the entity: the hierarchy is per-entitlement.
|
|
346
|
+
*
|
|
347
|
+
* Optional throughout. Skip it and the agent has no entity - calls still work, and checks and
|
|
348
|
+
* usage apply to the customer instead.
|
|
349
|
+
*/
|
|
350
|
+
export type AgentOptions = {
|
|
351
|
+
/** The durable agent. This is the entity budgets attach to. */
|
|
352
|
+
agentId: string;
|
|
353
|
+
/**
|
|
354
|
+
* The governance entity type to create the agent as. Required, and not defaulted: a guessed
|
|
355
|
+
* type pairs the agent with an attribution key nothing resolves, and the spend then lands on
|
|
356
|
+
* the customer looking exactly like a call that was never governed.
|
|
357
|
+
*/
|
|
358
|
+
entityTypeId: string;
|
|
359
|
+
displayName?: string;
|
|
360
|
+
metadata?: Record<string, string>;
|
|
361
|
+
/**
|
|
362
|
+
* What this agent may spend, and where it sits in the tree. Omit it and `register` writes an
|
|
363
|
+
* identity and nothing else.
|
|
364
|
+
*
|
|
365
|
+
* Grouped rather than flat because these five are one write underneath - an assignment over
|
|
366
|
+
* `(entity x entitlement)` - while the fields above are the entity. See {@link AgentBudget}.
|
|
367
|
+
*/
|
|
368
|
+
budget?: AgentBudget;
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* What an agent may spend, as the one write it actually is.
|
|
372
|
+
*
|
|
373
|
+
* Grouped because these are one write underneath - an assignment over `(entity x entitlement)` -
|
|
374
|
+
* while the fields beside them describe the entity. The grouping is what makes two illegal
|
|
375
|
+
* combinations unrepresentable: `apply` is one field, so there is no way to ask for both budget
|
|
376
|
+
* modes, and `entitlement` is required, so there is no way to write a limit against nothing.
|
|
377
|
+
*
|
|
378
|
+
* `parentId` lives here rather than on the entity because the hierarchy is **per-entitlement**:
|
|
379
|
+
* an agent can sit under one parent for one budget and elsewhere for another.
|
|
380
|
+
*/
|
|
381
|
+
export type AgentBudget = {
|
|
382
|
+
/**
|
|
383
|
+
* Which entitlement the budget is written against - a credit currency or a metered feature.
|
|
384
|
+
*
|
|
385
|
+
* Required, because an assignment is written over `(entity x entitlement)`: a limit with
|
|
386
|
+
* nothing to write it against has nowhere to land.
|
|
387
|
+
*/
|
|
388
|
+
entitlement: Entitlement;
|
|
389
|
+
/** ISO-8601 single-unit reset window, e.g. 'P1M'. Governance requires it when creating a budget. */
|
|
390
|
+
cadence?: string;
|
|
391
|
+
/** Places the agent under a parent entity. Tri-state: omit to leave it, null to make it a root. */
|
|
392
|
+
parentId?: string | null;
|
|
393
|
+
} & ({
|
|
394
|
+
/** The limit, written on every call. `null` clears whatever is assigned. */
|
|
395
|
+
limit?: number | null;
|
|
396
|
+
/** Writes the limit every time. The default. */
|
|
397
|
+
apply?: 'always';
|
|
398
|
+
} | {
|
|
399
|
+
/** The limit to seed with. Not nullable: there is nothing to clear on an agent being created. */
|
|
400
|
+
limit: number;
|
|
401
|
+
/**
|
|
402
|
+
* Writes the limit only when this agent has no assignment yet.
|
|
403
|
+
*
|
|
404
|
+
* Reach for it when an admin may change the budget out of band - `'always'` would overwrite
|
|
405
|
+
* their change on every process start.
|
|
406
|
+
*/
|
|
407
|
+
apply: 'on-create';
|
|
408
|
+
});
|
|
409
|
+
export type AgentScope = {
|
|
410
|
+
/** The tenant. Every governance call is scoped to one customer. */
|
|
411
|
+
customerId: string;
|
|
412
|
+
};
|
|
413
|
+
/** `register` - the standalone call, which has no run to take the scope from. */
|
|
414
|
+
export type RegisterInput = AgentOptions & AgentScope;
|
|
415
|
+
/** `deregister` - archiving the entity. No entitlement: an entity is not archived per-entitlement. */
|
|
416
|
+
export type DeregisterInput = Pick<AgentScope, 'customerId'> & Pick<AgentOptions, 'agentId'>;
|
|
417
|
+
/**
|
|
418
|
+
* Every property path the shipped recipes gate, written as the overlay walks it.
|
|
419
|
+
*
|
|
420
|
+
* Paths, not leaf names. The extra field has to appear in the vendor's own parameter type or an
|
|
421
|
+
* object literal carrying it is an excess-property error - but matching on the last segment alone
|
|
422
|
+
* also typed `stiggContext` onto every *other* method that happened to share the name. `create` is
|
|
423
|
+
* the common one: `openai.files.create` and `openai.batches.create` are not gated by any recipe,
|
|
424
|
+
* so a caller who passed a context there type-checked, was never checked, and handed the vendor an
|
|
425
|
+
* unknown field it would forward rather than strip - the exact harm the narrow list existed to
|
|
426
|
+
* prevent.
|
|
427
|
+
*
|
|
428
|
+
* `messages.stream` is here without being in any recipe's `methods`: it reaches the wire through
|
|
429
|
+
* the gated `messages.create`, so a context given to it does arrive.
|
|
430
|
+
*/
|
|
431
|
+
export type GatedPath = ['messages', 'create'] | ['messages', 'stream'] | ['chat', 'completions', 'create'] | ['responses', 'create'] | ['systemOne'] | ['models', 'generateContent'] | ['models', 'generateContentStream'] | ['chat', 'complete'] | ['chat', 'stream'] | ['chat', 'parse'] | ['chat', 'parseStream'] | ['fim', 'complete'] | ['fim', 'stream'] | ['agents', 'complete'] | ['agents', 'stream'];
|
|
432
|
+
/**
|
|
433
|
+
* Gemini's conversational surface, which is wrapped as an object in its own right: a `Chat` holds
|
|
434
|
+
* the models module privately, so wrapping the client never reaches these. The paths are one
|
|
435
|
+
* segment long because the `Chat` *is* the client there.
|
|
436
|
+
*
|
|
437
|
+
* Kept OUT of {@link GatedPath} and reached only through {@link ChatOf}, because one segment is
|
|
438
|
+
* the whole path here and `sendMessage` is far too common a verb to claim globally. In the
|
|
439
|
+
* fallback list it typed `stiggContext` onto any client that happened to have one - a Slack
|
|
440
|
+
* client, a queue client - and on a client that some *other* recipe matched, that was the silent
|
|
441
|
+
* case the narrow list exists to prevent: `wrap` succeeded, the method was never gated, and the
|
|
442
|
+
* context object was forwarded to the vendor as an unknown field.
|
|
443
|
+
*/
|
|
444
|
+
export type ChatGatedPath = ['sendMessage'] | ['sendMessageStream'];
|
|
445
|
+
/**
|
|
446
|
+
* Ollama's two, kept out of {@link GatedPath} for exactly the reason `sendMessage` is.
|
|
447
|
+
*
|
|
448
|
+
* The client is flat - `chat` and `generate` are methods on it, with no namespace to make them
|
|
449
|
+
* distinctive - and those are among the most common verbs a client object can have. In the
|
|
450
|
+
* fallback list they would type `stiggContext` onto any object with a `chat(...)` method at all,
|
|
451
|
+
* on clients no recipe matches, which is the silent case that list exists to prevent: `wrap`
|
|
452
|
+
* succeeds, the method is never gated, and the context object is forwarded to the vendor as an
|
|
453
|
+
* unknown field. Reached only through {@link ChatOf}, keyed on all three markers
|
|
454
|
+
* `ollamaRecipe.matches` requires.
|
|
455
|
+
*/
|
|
456
|
+
export type OllamaGatedPath = ['chat'] | ['generate'];
|
|
457
|
+
/**
|
|
458
|
+
* The paths to overlay when no recipe was named, given what the client actually looks like.
|
|
459
|
+
*
|
|
460
|
+
* `getHistory` is the discriminator, and it is exactly what `geminiChatRecipe.matches` requires -
|
|
461
|
+
* so the types promise a context on precisely the methods the runtime will gate, rather than on
|
|
462
|
+
* every object with a method of that name.
|
|
463
|
+
*/
|
|
464
|
+
/**
|
|
465
|
+
* The shapes the registry matches *before* it ever reaches the chat recipe.
|
|
466
|
+
*
|
|
467
|
+
* `geminiChatRecipe` is last in the shipped list, so any of these wins - which means a client
|
|
468
|
+
* carrying one of them has its chat methods left unpatched however chat-shaped it also looks.
|
|
469
|
+
*/
|
|
470
|
+
type GatedVendorShape = {
|
|
471
|
+
messages: {
|
|
472
|
+
create: unknown;
|
|
473
|
+
};
|
|
474
|
+
} | {
|
|
475
|
+
chat: {
|
|
476
|
+
completions: {
|
|
477
|
+
create: unknown;
|
|
478
|
+
};
|
|
479
|
+
};
|
|
480
|
+
} | {
|
|
481
|
+
responses: {
|
|
482
|
+
create: unknown;
|
|
483
|
+
};
|
|
484
|
+
} | {
|
|
485
|
+
models: {
|
|
486
|
+
generateContent: unknown;
|
|
487
|
+
};
|
|
488
|
+
} | {
|
|
489
|
+
models: {
|
|
490
|
+
generateContentStream: unknown;
|
|
491
|
+
};
|
|
492
|
+
} | {
|
|
493
|
+
chat: {
|
|
494
|
+
complete: unknown;
|
|
495
|
+
};
|
|
496
|
+
} | {
|
|
497
|
+
systemOne: unknown;
|
|
498
|
+
};
|
|
499
|
+
/**
|
|
500
|
+
* Which paths to overlay when no recipe was named, given what the client looks like.
|
|
501
|
+
*
|
|
502
|
+
* `getHistory` is the discriminator, and it is what `geminiChatRecipe.matches` requires - so the
|
|
503
|
+
* types promise a context on the methods the runtime will gate rather than on every object with
|
|
504
|
+
* a method of that name.
|
|
505
|
+
*
|
|
506
|
+
* It is not enough on its own, though. A client carrying *both* a gated vendor's shape and a
|
|
507
|
+
* chat's is matched as that vendor, leaving `sendMessage` unpatched - and an overlay keyed on
|
|
508
|
+
* `getHistory` alone still invited a context onto it, which is never checked and is handed to
|
|
509
|
+
* the vendor as an unknown field. So anything the registry would claim first is excluded here,
|
|
510
|
+
* mirroring that precedence.
|
|
511
|
+
*/
|
|
512
|
+
export type ChatOf<T> = T extends GatedVendorShape ? GatedPath : T extends OllamaShape ? GatedPath | OllamaGatedPath : T extends {
|
|
513
|
+
getHistory: (...args: any[]) => any;
|
|
514
|
+
} ? GatedPath | ChatGatedPath : GatedPath;
|
|
515
|
+
/** What `ollamaRecipe.matches` looks for, as a type. Kept in step with it by hand. */
|
|
516
|
+
type OllamaShape = {
|
|
517
|
+
chat: (...args: any[]) => any;
|
|
518
|
+
generate: (...args: any[]) => any;
|
|
519
|
+
ps: (...args: any[]) => any;
|
|
520
|
+
};
|
|
521
|
+
/**
|
|
522
|
+
* The paths still live after stepping through `K`, or `never` when that key leads nowhere gated.
|
|
523
|
+
*
|
|
524
|
+
* This is what bounds the walk: recursion follows the paths themselves, so there is no depth
|
|
525
|
+
* budget to keep in step with the deepest vendor surface, and no mapping of branches - a client's
|
|
526
|
+
* whole `files` or `batches` subtree - that could never hold a gated method.
|
|
527
|
+
*/
|
|
528
|
+
type Below<Paths extends readonly string[], K> = Paths extends readonly [infer Head, ...infer Rest] ? Head extends K ? Rest extends readonly string[] ? Rest : never : never : never;
|
|
529
|
+
/** One signature, with the context threaded into whatever parameter object it already takes. */
|
|
530
|
+
type GatedSignature<P, R extends unknown[], Ret, Ctx> = P extends object ? (params: P & {
|
|
531
|
+
stiggContext?: Ctx;
|
|
532
|
+
}, ...rest: R) => Ret : (params: P, ...rest: R) => Ret;
|
|
533
|
+
/**
|
|
534
|
+
* A gated method, overloads and all.
|
|
535
|
+
*
|
|
536
|
+
* The arms are tried widest first because a single `infer` arm would silently collapse an
|
|
537
|
+
* overloaded method to its **last** signature - and `chat.completions.create` is overloaded
|
|
538
|
+
* precisely on `stream`, so collapsing it would hand a streaming caller the non-streaming return
|
|
539
|
+
* type. Three arms covers every vendor method the recipes gate; a fourth overload would fall
|
|
540
|
+
* through to the last arm and keep working, minus the earlier signatures.
|
|
541
|
+
*/
|
|
542
|
+
type GatedMethod<F, Ctx> = F extends {
|
|
543
|
+
(params: infer P1, ...rest: infer R1): infer T1;
|
|
544
|
+
(params: infer P2, ...rest: infer R2): infer T2;
|
|
545
|
+
(params: infer P3, ...rest: infer R3): infer T3;
|
|
546
|
+
} ? GatedSignature<P1, R1, T1, Ctx> & GatedSignature<P2, R2, T2, Ctx> & GatedSignature<P3, R3, T3, Ctx> : F extends {
|
|
547
|
+
(params: infer P1, ...rest: infer R1): infer T1;
|
|
548
|
+
(params: infer P2, ...rest: infer R2): infer T2;
|
|
549
|
+
} ? GatedSignature<P1, R1, T1, Ctx> & GatedSignature<P2, R2, T2, Ctx> : F extends (params: infer P, ...rest: infer R) => infer Ret ? GatedSignature<P, R, Ret, Ctx> : F;
|
|
550
|
+
/**
|
|
551
|
+
* The client's own surface, with every gated method taught to accept a `stiggContext`.
|
|
552
|
+
*
|
|
553
|
+
* Rebuilt structurally, which on its own would lose the vendor's class identity - so
|
|
554
|
+
* {@link WrappedClient} intersects it back with `T` rather than replacing it. The overlay comes
|
|
555
|
+
* first in that intersection on purpose: an intersection of two call signatures is an overload
|
|
556
|
+
* set resolved in order, and the vendor's own signature would reject `stiggContext` as an excess
|
|
557
|
+
* property if it were tried first.
|
|
558
|
+
*/
|
|
559
|
+
export type GatedSurface<T, Ctx, Paths extends readonly string[] = GatedPath> = {
|
|
560
|
+
[K in keyof T]: [Below<Paths, K>] extends [never] ? T[K] : [
|
|
561
|
+
] extends Below<Paths, K> ? GatedMethod<T[K], Ctx> : GatedSurface<T[K], Ctx, Below<Paths, K>>;
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* Which paths a wrapped client accepts a context on: the recipe's own, or the shipped ones.
|
|
565
|
+
*
|
|
566
|
+
* `wrap` infers the options object it was handed, so a recipe passed inline keeps whatever types
|
|
567
|
+
* it was written with - and a recipe that declares its paths as literals (`path: [...] as const`)
|
|
568
|
+
* gets the overlay on exactly the methods it gates, rather than on the shipped vendors' paths.
|
|
569
|
+
*
|
|
570
|
+
* Anything else falls back to {@link GatedPath}: no recipe at all, which is the detection case, and
|
|
571
|
+
* a recipe whose paths widened to `string[]`, where there is nothing to narrow to and guessing
|
|
572
|
+
* would be worse than the default. So this only ever adds precision; it cannot take it away.
|
|
573
|
+
*/
|
|
574
|
+
export type PathsOf<R, T = unknown> = R extends {
|
|
575
|
+
methods: readonly {
|
|
576
|
+
path: infer P;
|
|
577
|
+
}[];
|
|
578
|
+
} ? P extends readonly [string, ...string[]] ? P : ChatOf<T> : ChatOf<T>;
|
|
579
|
+
/**
|
|
580
|
+
* What a call still owes, given what `wrap` was already told.
|
|
581
|
+
*
|
|
582
|
+
* `customerId` is the one field that has to come from somewhere, so a client wrapped without one
|
|
583
|
+
* types it as required on every call and a client wrapped with one types it as optional. The
|
|
584
|
+
* runtime checks it either way, for callers the types never reached.
|
|
585
|
+
*/
|
|
586
|
+
export type CallContext<Wrapped> = Wrapped extends {
|
|
587
|
+
customerId: string;
|
|
588
|
+
} ? StiggContext : StiggContext & {
|
|
589
|
+
customerId: string;
|
|
590
|
+
};
|
|
591
|
+
/**
|
|
592
|
+
* A vendor client bound to this environment. Every call its recipe covers is checked before it
|
|
593
|
+
* leaves and metered after, against the context `wrap` was given and whatever the call overrides.
|
|
594
|
+
*/
|
|
595
|
+
export type WrappedClient<T, Wrapped = StiggContext, Paths extends readonly string[] = GatedPath> = GatedSurface<T, CallContext<Wrapped>, Paths> & T & {
|
|
596
|
+
/** What this client did, and what is still in flight. */
|
|
597
|
+
readonly stigg: ClientGovernance;
|
|
598
|
+
};
|
|
599
|
+
/** The governance handle hung off a wrapped client. */
|
|
600
|
+
export type ClientGovernance = {
|
|
601
|
+
/** Waits for in-flight ingests, then reports what this client did. */
|
|
602
|
+
flush(): Promise<SessionSummary>;
|
|
603
|
+
/** Drains, then releases what this client holds. */
|
|
604
|
+
close(): Promise<void>;
|
|
605
|
+
/** What this client has done so far, without waiting. */
|
|
606
|
+
summary(): SessionSummary;
|
|
607
|
+
/** The last decision this client made, or undefined if it has made none. */
|
|
608
|
+
readonly lastDecision?: AccessDecision;
|
|
609
|
+
/** The `x-stigg-*` attribution for work continuing in another process. */
|
|
610
|
+
headers(override?: StiggContext): GovernanceHeaders;
|
|
611
|
+
/** The gate, for a call this client does not wrap. */
|
|
612
|
+
check(override?: StiggContext, requestedUsage?: number): Promise<AccessDecision>;
|
|
613
|
+
/** The meter, for usage this client did not observe. */
|
|
614
|
+
report(metrics: Metrics, override?: StiggContext): Promise<IngestResult>;
|
|
615
|
+
};
|
|
616
|
+
/** What an out-of-process ingest needs, since it has no run to carry it. */
|
|
617
|
+
export type IngestContext = {
|
|
618
|
+
/** What the run this usage belongs to checks against. Defaults to the `credits` currency. */
|
|
619
|
+
entitlement?: Entitlement;
|
|
620
|
+
/**
|
|
621
|
+
* What to report. Defaults to the vendor's own names, read off the usage object the proxy
|
|
622
|
+
* observed - the same default a wrapped client gets.
|
|
623
|
+
*/
|
|
624
|
+
metrics?: MetricsMapper;
|
|
625
|
+
/**
|
|
626
|
+
* Which shape the observed usage is. Named rather than sniffed: Anthropic and the Responses API
|
|
627
|
+
* both count `input_tokens`, and a wrong guess would report to another customer's meter.
|
|
628
|
+
* Omitted, only the normalized counters are available - so supply a `metrics` mapper too.
|
|
629
|
+
*/
|
|
630
|
+
vendor?: ObservedVendor;
|
|
631
|
+
/** Which of Stigg's ingest APIs to report through. Defaults to the client's setting. */
|
|
632
|
+
ingest?: IngestMode;
|
|
633
|
+
/**
|
|
634
|
+
* What the event is called in `'async'` mode. There is no wrapped call out here to name it
|
|
635
|
+
* after, so a proxy either says, or is reported under the vendor it declared.
|
|
636
|
+
*
|
|
637
|
+
* Singular, and named to match `IngestOptions.event`: one report is one event, whatever the
|
|
638
|
+
* turn measured.
|
|
639
|
+
*/
|
|
640
|
+
event?: EventIngest;
|
|
641
|
+
};
|
|
642
|
+
export {};
|