@elysra/tungsten-runtime 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,667 @@
1
+ /**
2
+ * The contract between SDKs generated by tungsten and this runtime
3
+ * (planning/05 "TypeScript SDK", planning/06 "Runtime and safety").
4
+ *
5
+ * Generated code is mostly data: one `OperationDescriptor` per operation,
6
+ * one `ApiDescriptor` per client, and thin typed wrappers that call
7
+ * `ClientCore`. Everything with behaviour (validation, auth, idempotency,
8
+ * retries, error envelopes, previews, verification, pagination) lives in the
9
+ * runtime, so a fix ships as a runtime release instead of a regeneration.
10
+ *
11
+ * PHASE-2 CONTRACT: these types are shared by the runtime and the
12
+ * TypeScript emitter. Additive changes only (new optional fields, new
13
+ * union members), and only with both sides updated.
14
+ */
15
+ import type { TokenStore } from "./oauth.js";
16
+ import type { EventStream } from "./streams.js";
17
+ export type Safety = "read_only" | "mutating" | "destructive" | "irreversible";
18
+ export type Retryable = "never" | "after_delay" | "same_key_only" | "after_remediation";
19
+ /** Closed set of error categories shared by every tungsten runtime. */
20
+ export type Category = "VALIDATION_FAILED" | "MALFORMED_REQUEST" | "REQUEST_TOO_LARGE" | "AUTH_FAILED" | "NOT_FOUND" | "CONFLICT" | "PRECONDITION_FAILED" | "RATE_LIMITED" | "UPSTREAM_UNAVAILABLE" | "OUTCOME_UNKNOWN" | "TRANSPORT_FAILED" | "CONFIRMATION_REQUIRED" | "GATE_DISABLED" | "UNEXPECTED_RESPONSE";
21
+ /** The diagnostic error envelope (planning/06). Field names are snake_case
22
+ * on purpose: the envelope is pasted verbatim into a model's context and is
23
+ * identical across the TypeScript, Python and Rust runtimes. */
24
+ export interface Diagnostic {
25
+ status: "error";
26
+ category: Category;
27
+ /** Operation id, e.g. `public.replayWebhookDelivery`. */
28
+ operation: string;
29
+ http_status: number | null;
30
+ /** The API's error code from the response body, when present. */
31
+ code: string | null;
32
+ /** JSON path of the offending argument (`body.recipient_e164`). */
33
+ failed_parameter: string | null;
34
+ /** Redacted when the parameter is sensitive; strings cut to 200 chars. */
35
+ received_value: unknown;
36
+ expected: string | null;
37
+ remediation: string;
38
+ retryable: Retryable;
39
+ retry_after_ms: number | null;
40
+ /** What to do next, e.g. the verification operation to call. */
41
+ next_action: string | null;
42
+ request_id: string | null;
43
+ trace: {
44
+ attempts: number;
45
+ };
46
+ }
47
+ export interface ResponseMeta {
48
+ status: number;
49
+ headers: Record<string, string>;
50
+ requestId: string | null;
51
+ attempts: number;
52
+ }
53
+ /** The meta of a stream item: the answer of the connection the event
54
+ * arrived on (the last one, after a reconnect) and how often the stream
55
+ * reconnected up to this event. */
56
+ export interface StreamMeta extends ResponseMeta {
57
+ reconnects: number;
58
+ }
59
+ export interface Verification {
60
+ checked: boolean;
61
+ /** True when `expect` held; false when it did not within the budget. */
62
+ passed: boolean;
63
+ /** The verification operation's last result body. */
64
+ observed: unknown;
65
+ /** True when polling for `terminal` ran out of budget. */
66
+ timedOut?: boolean;
67
+ /** Why the check could not run (verification call failed); `checked` is false. */
68
+ error?: Diagnostic;
69
+ }
70
+ /** The outcome of a call. A success response without a body (204, or any
71
+ * success status the spec declares without content) has `value: undefined`;
72
+ * generated SDKs type such operations as `void` (or `T | undefined` when the
73
+ * operation can also answer with a body). */
74
+ export type Result<T> = {
75
+ ok: true;
76
+ value: T;
77
+ meta: ResponseMeta;
78
+ verification?: Verification;
79
+ } | {
80
+ ok: false;
81
+ error: Diagnostic;
82
+ /** What the failed call or macro already produced, when its effect
83
+ * happened: the success body of a mutation whose response failed
84
+ * strict validation, or a macro's completed step results by their
85
+ * `as` names. It can hold values the API shows only once (a signing
86
+ * secret), so store them before acting on the error; they are never
87
+ * repeated in the envelope. */
88
+ partial?: unknown;
89
+ };
90
+ export type HttpMethod = "GET" | "PUT" | "POST" | "DELETE" | "OPTIONS" | "HEAD" | "PATCH" | "TRACE";
91
+ export type ParamLocation = "path" | "query" | "header" | "cookie";
92
+ export type ParamStyle = "simple" | "form" | "label" | "matrix" | "space_delimited" | "pipe_delimited" | "deep_object";
93
+ export type ParamRole = "plain" | "idempotency_key" | "dry_run" | "origin" | "auth" | "constant";
94
+ export interface ParamDescriptor {
95
+ /** Key in the generated method's args object. Roles `idempotency_key`,
96
+ * `origin`, `auth` and `constant` are not args: they come from
97
+ * `CallOptions.idempotencyKey` and the auth profile (`ClientOptions.auth`),
98
+ * and their `name` is informational. */
99
+ name: string;
100
+ /** Name on the wire. */
101
+ wire: string;
102
+ in: ParamLocation;
103
+ required: boolean;
104
+ style: ParamStyle;
105
+ explode: boolean;
106
+ role: ParamRole;
107
+ /** Header value of a `constant` parameter: the runtime sends it on every
108
+ * call (`ClientOptions.headers` and `CallOptions.headers` replace it). */
109
+ constant?: string;
110
+ sensitive?: boolean;
111
+ }
112
+ export type BodyEncoding = "json" | "form" | "multipart" | "bytes" | "text";
113
+ export interface BodyDescriptor {
114
+ mediaType: string;
115
+ encoding: BodyEncoding;
116
+ required: boolean;
117
+ /** `merged`: the body is a JSON object whose fields are keys of the args
118
+ * object, listed in `fields` (wire names; TypeScript models keep wire
119
+ * names). `arg`: the whole body is `args[arg]` (bytes, text, unions,
120
+ * arrays, or field names that collide with a parameter). */
121
+ shape: {
122
+ kind: "merged";
123
+ fields: string[];
124
+ } | {
125
+ kind: "arg";
126
+ arg: string;
127
+ };
128
+ }
129
+ export type StatusMatch = number | "1XX" | "2XX" | "3XX" | "4XX" | "5XX" | "default";
130
+ export interface ResponseDescriptor {
131
+ status: StatusMatch;
132
+ kind: "success" | "error" | "ambiguous";
133
+ /** First media type, or null for a bare status. */
134
+ mediaType: string | null;
135
+ }
136
+ export type PaginationDescriptor = {
137
+ style: "cursor";
138
+ requestParam: string;
139
+ /** Response field holding the next cursor; empty when only `cursorItemField` applies. */
140
+ responseField: string;
141
+ itemsField: string;
142
+ pageSizeParam: string | null;
143
+ /** Response boolean (`has_more`): iteration stops as soon as it is `false`. */
144
+ hasMoreField?: string;
145
+ /** Without a cursor in the response, the next cursor is this field of the last item. */
146
+ cursorItemField?: string;
147
+ } | {
148
+ style: "offset";
149
+ offsetParam: string;
150
+ limitParam: string;
151
+ itemsField: string;
152
+ } | {
153
+ style: "page";
154
+ pageParam: string;
155
+ sizeParam: string;
156
+ itemsField: string;
157
+ } | {
158
+ style: "link_header";
159
+ itemsField: string;
160
+ };
161
+ export type IdempotencyKind = "none" | "auto" | "caller_owned" | "content_hash" | "content_identity";
162
+ export interface RemediationEntry {
163
+ category?: Category;
164
+ text?: string;
165
+ retryable?: Retryable;
166
+ next_action?: string;
167
+ }
168
+ export interface VerifyDescriptor {
169
+ /** Operation id of the read operation to call. */
170
+ operation: string;
171
+ /** Argument mapping, keyed by the read operation's parameter wire names
172
+ * (or generated names); string values starting with `$response.` or
173
+ * `$args.` are resolved against the call, `$args` by generated or wire
174
+ * name. */
175
+ args: Record<string, unknown>;
176
+ /** Field path → predicate (`{in: [...]}`, `{equals: x}`, `{contains: {...}}`).
177
+ * `$response.` and `$args.` references in `expect` and `terminal` are
178
+ * resolved against the call; an unresolved one never matches. */
179
+ expect: Record<string, unknown>;
180
+ terminal: Record<string, unknown>;
181
+ pollIntervalMs: number | null;
182
+ pollBudgetMs: number | null;
183
+ }
184
+ export interface AgentMeta {
185
+ safety: Safety;
186
+ idempotency: {
187
+ policy: IdempotencyKind;
188
+ header: string | null;
189
+ format: string | null;
190
+ persistRequired: boolean;
191
+ note: string | null;
192
+ };
193
+ preview: {
194
+ mode: "local";
195
+ } | {
196
+ mode: "header";
197
+ header: string;
198
+ value: string;
199
+ } | {
200
+ mode: "endpoint";
201
+ operation: string;
202
+ } | {
203
+ mode: "none";
204
+ };
205
+ confirmation: {
206
+ summaryFields: string[];
207
+ message: string | null;
208
+ } | null;
209
+ verify: VerifyDescriptor | null;
210
+ /** Operation-specific remediation by API error code. */
211
+ remediation: Record<string, RemediationEntry>;
212
+ remediationNote: string | null;
213
+ sensitiveResponseFields: string[];
214
+ /** Arguments marked sensitive (`x-agent-sensitive` on request body
215
+ * fields), as dotted paths into the args object (`pin`, `body.pin`,
216
+ * `profile.recovery_phrase`; array items are not indexed). Redacted in
217
+ * `received_value`, previews, middleware and diagnostics. */
218
+ sensitiveRequestFields?: string[];
219
+ shownOnce: boolean;
220
+ }
221
+ /** Structural subset of a Zod schema (`safeParse`), so the runtime does not
222
+ * depend on a Zod version. */
223
+ export interface SchemaLike<T = unknown> {
224
+ safeParse(value: unknown): {
225
+ success: true;
226
+ data: T;
227
+ } | {
228
+ success: false;
229
+ error: {
230
+ issues: Array<{
231
+ path: Array<string | number>;
232
+ message: string;
233
+ code?: string;
234
+ }>;
235
+ };
236
+ };
237
+ }
238
+ export interface OperationDescriptor {
239
+ /** IR operation id (`public.replayWebhookDelivery`). */
240
+ id: string;
241
+ method: HttpMethod;
242
+ /** Path template as in the spec (`/v1/webhooks/{endpoint_id}/rotate`);
243
+ * placeholders use wire names. */
244
+ path: string;
245
+ params: ParamDescriptor[];
246
+ body: BodyDescriptor | null;
247
+ responses: ResponseDescriptor[];
248
+ /** OR of AND-sets of auth scheme names. Empty: no auth. */
249
+ security: string[][];
250
+ pagination: PaginationDescriptor | null;
251
+ /** For rpc-unflattened operations: the envelope field and value to send,
252
+ * and the field the args are placed under. `constants` are further
253
+ * envelope members sent verbatim (JSON-RPC `jsonrpc: "2.0"`). */
254
+ rpc: {
255
+ field: string;
256
+ value: string;
257
+ paramsField: string;
258
+ constants?: Record<string, unknown>;
259
+ } | null;
260
+ /** Field path of the API error code in error bodies (`code`, `error.code`). */
261
+ errorCodeField: string | null;
262
+ status: {
263
+ kind: "implemented";
264
+ } | {
265
+ kind: "gated";
266
+ envVar: string;
267
+ disabledStatus: number;
268
+ };
269
+ agent: AgentMeta;
270
+ /** Validates the args object before any network call. */
271
+ request?: SchemaLike;
272
+ /** Validates the success body (per `ClientOptions.validateResponses`).
273
+ *
274
+ * Untagged unions (IR `UnionStrategy::Untagged`, TG0301) are emitted as
275
+ * `z.union([...])` with the variants in IR order, and Zod tries them in
276
+ * that order: the body is accepted when any variant accepts it, and the
277
+ * first variant that does decides which refinements applied. The
278
+ * result's `value` is always the decoded wire body itself, never the
279
+ * schema's output, so an earlier variant that overlaps a later one (or
280
+ * strips unknown members) cannot drop or rewrite members of the body. */
281
+ response?: SchemaLike;
282
+ /** One-line summary (compacted description); the last line of preview effects. */
283
+ summary?: string | null;
284
+ /** The operation's event stream (`ClientCore.stream`); absent when it has none. */
285
+ stream?: StreamDescriptor;
286
+ }
287
+ /** The server-sent events of an operation (IR `StreamSpec`). */
288
+ export interface StreamDescriptor {
289
+ /** Validates the decoded `data` of every event (per
290
+ * `ClientOptions.validateResponses`); absent: events are any JSON value. */
291
+ event?: SchemaLike;
292
+ /** A `data` value that ends the stream without being an event (`[DONE]`). */
293
+ done?: string;
294
+ /** Wire name of the boolean request body field that selects the stream;
295
+ * `ClientCore.stream` sets it to `true`. */
296
+ flag?: string;
297
+ }
298
+ /** The `authorizationCode` flow of an OAuth2 scheme (present only when the
299
+ * spec declares one). `refreshUrl` falls back to `tokenUrl`. */
300
+ export interface AuthorizationCodeDescriptor {
301
+ authorizationUrl: string;
302
+ tokenUrl: string;
303
+ refreshUrl: string | null;
304
+ scopes: string[];
305
+ }
306
+ export type AuthSchemeDescriptor = {
307
+ kind: "api_key";
308
+ name: string;
309
+ in: "header" | "query" | "cookie";
310
+ wire: string;
311
+ }
312
+ /** `prefix`: the token must start with it (tungsten.yml
313
+ * `auth_profiles.<name>.bearer.prefix`, e.g. `ztw_`); checked before any
314
+ * request is sent, and a mismatch is `AUTH_FAILED` with zero network calls. */
315
+ | {
316
+ kind: "http_bearer";
317
+ name: string;
318
+ prefix: string | null;
319
+ } | {
320
+ kind: "http_basic";
321
+ name: string;
322
+ } | {
323
+ kind: "oauth2";
324
+ name: string;
325
+ tokenUrl: string | null;
326
+ scopes: string[];
327
+ authorizationCode?: AuthorizationCodeDescriptor;
328
+ } | {
329
+ kind: "composite";
330
+ name: string;
331
+ /** Spec scheme names this profile satisfies together. */
332
+ satisfies: string[];
333
+ parts: Array<{
334
+ kind: "cookie";
335
+ name: string;
336
+ } | {
337
+ kind: "header";
338
+ name: string;
339
+ equalsCookie: string | null;
340
+ fromConfig: string | null;
341
+ mutationOnly: boolean;
342
+ } | {
343
+ kind: "bearer";
344
+ prefix: string | null;
345
+ }>;
346
+ };
347
+ export interface ApiDescriptor {
348
+ /** Machine name (`zrotext`). */
349
+ name: string;
350
+ /** SDK package version. */
351
+ version: string;
352
+ tungstenVersion: string;
353
+ servers: string[];
354
+ auth: AuthSchemeDescriptor[];
355
+ /** Global remediation by API error code (agent.yml `errors.codes`). */
356
+ errorCodes: Record<string, RemediationEntry>;
357
+ /** Statuses that make a mutation's outcome unknown. */
358
+ ambiguousStatuses: number[];
359
+ /** Non-JSON error mapping (agent.yml `errors.non_json`); `media` is a
360
+ * media type or `"none"` for a bare status. */
361
+ nonJson: Array<{
362
+ status: number;
363
+ media: string;
364
+ category: Category;
365
+ retryable: Retryable;
366
+ text: string | null;
367
+ }>;
368
+ /** Runtime gate env var → explanation. */
369
+ gates: Record<string, string>;
370
+ /** Retry defaults by safety tier (agent.yml `defaults.retries`):
371
+ * `readOnly` for read-only operations, `mutating` for every other tier
372
+ * (a mutation is still retried only with an idempotency key or an
373
+ * identity body). `ClientOptions.retries` overrides them field by field.
374
+ * Absent: the runtime defaults apply to every tier. */
375
+ retries?: {
376
+ readOnly: Partial<RetryOptions>;
377
+ mutating: Partial<RetryOptions>;
378
+ };
379
+ }
380
+ /** Credentials per auth scheme name. A string is the secret of an
381
+ * `api_key`, `http_bearer` or `http_basic` (`user:password`) scheme. A
382
+ * composite profile takes an object keyed by cookie name or config key
383
+ * (for ZROtext's owner session: the two cookie values and `origin`). */
384
+ export type AuthConfig = Record<string, string | Record<string, string>>;
385
+ export interface IdempotencyStore {
386
+ get(scope: string, logicalId: string): Promise<string | undefined>;
387
+ put(scope: string, logicalId: string, key: string): Promise<void>;
388
+ }
389
+ /** What middleware sees of one attempt. Secrets are redacted: secret
390
+ * headers, auth query values, and in a JSON or form body the arguments
391
+ * marked sensitive (or named like credentials) as `<redacted>`. */
392
+ export interface RequestContext {
393
+ operation: OperationDescriptor;
394
+ attempt: number;
395
+ method: HttpMethod;
396
+ url: string;
397
+ headers: Record<string, string>;
398
+ body: BodyInit | null;
399
+ }
400
+ export interface Middleware {
401
+ onRequest?(ctx: RequestContext): void | Promise<void>;
402
+ onResponse?(ctx: RequestContext, response: Response): void | Promise<void>;
403
+ onRetry?(ctx: RequestContext, reason: Diagnostic): void | Promise<void>;
404
+ }
405
+ export interface RetryOptions {
406
+ /** Attempts after the first, for retryable outcomes. */
407
+ max: number;
408
+ baseMs: number;
409
+ maxMs: number;
410
+ jitter: "none" | "full" | "equal";
411
+ honorRetryAfter: boolean;
412
+ }
413
+ export interface ClientOptions {
414
+ /** Defaults to the first server. */
415
+ baseUrl?: string;
416
+ auth?: AuthConfig;
417
+ /** Holds the tokens of `authorizationCode` schemes (configure such a
418
+ * scheme as `{flow: "authorizationCode", clientId, clientSecret?,
419
+ * redirectUri?}`). Default: in memory, per client. */
420
+ tokenStore?: TokenStore;
421
+ /** Defaults to `globalThis.fetch`. */
422
+ fetch?: typeof fetch;
423
+ /** Per-attempt timeout. Default 30000. */
424
+ timeoutMs?: number;
425
+ /** Overrides the API's per-tier retry defaults (`ApiDescriptor.retries`)
426
+ * field by field, for every tier. */
427
+ retries?: Partial<RetryOptions>;
428
+ idempotencyStore?: IdempotencyStore;
429
+ middleware?: Middleware[];
430
+ /** Default `warn`: report through `onDiagnostic`, never fail the call. */
431
+ validateResponses?: "off" | "warn" | "strict";
432
+ /** Receives warnings (response validation, deprecations). */
433
+ onDiagnostic?: (d: Diagnostic) => void;
434
+ /** Extra headers on every request. */
435
+ headers?: Record<string, string>;
436
+ /** Largest server-sent event `stream()` accepts, in UTF-8 bytes (the field
437
+ * lines of one event plus the line being read). A larger one ends the
438
+ * stream with an `UNEXPECTED_RESPONSE` failure. Default 1048576 (1 MiB). */
439
+ maxEventBytes?: number;
440
+ /** How often `stream()` reconnects a dropped stream (a lost connection, or
441
+ * a clean end before the `done` event the operation declares) with
442
+ * `Last-Event-ID` set to the last event id it delivered; events whose id
443
+ * was delivered are never delivered twice. Only for operations that are
444
+ * safe to repeat (read-only, or with replay protection) and only once
445
+ * an event id is known (or before the first event). 0 disables it.
446
+ * Default 3. */
447
+ maxReconnects?: number;
448
+ /** Longest wait before a reconnect, in milliseconds: the server's `retry`
449
+ * value (250 ms when it sent none) is capped at this and then reduced by
450
+ * up to 25 % at random. Default 30000. */
451
+ reconnectMaxMs?: number;
452
+ /** A stream that delivers no bytes (comments count) for this many
453
+ * milliseconds is idle: it is reconnected like a dropped one, else ends
454
+ * with a failure whose `code` is `STREAM_IDLE`. Default: the per-attempt
455
+ * `timeoutMs`, which ends the stream without a reconnect. */
456
+ idleTimeoutMs?: number;
457
+ /** Overrides the key used to sign confirmation tokens. Default: random
458
+ * per client instance. */
459
+ confirmationKey?: Uint8Array;
460
+ /** Operations the core can resolve by id: verification hooks, endpoint
461
+ * previews and macro steps. Operations passed to `call` are added too. */
462
+ operations?: OperationDescriptor[];
463
+ /** Clock in epoch milliseconds (token expiry, Retry-After dates). Default `Date.now`. */
464
+ now?: () => number;
465
+ /** Random number in [0, 1) for retry jitter. Default `Math.random`. */
466
+ random?: () => number;
467
+ }
468
+ export interface CallOptions {
469
+ /** Required for `caller_owned` operations with `persistRequired`. */
470
+ idempotencyKey?: string;
471
+ /** Token from `preview()`; required for `irreversible`, and for
472
+ * `destructive` unless `true` is given. A token authorizes one intent:
473
+ * the first call that sends spends it, and it is accepted again only for
474
+ * a retry with the same idempotency key (or the identical body of a
475
+ * `content_identity` operation). */
476
+ confirm?: string | true;
477
+ /** `false`: `confirm: true` does not confirm a destructive operation
478
+ * either, and remediation never offers it (callers that only forward
479
+ * preview tokens, such as an MCP server). Default `true`. */
480
+ allowConfirmTrue?: boolean;
481
+ signal?: AbortSignal;
482
+ timeoutMs?: number;
483
+ /** Optional hard response-body byte cap. Positive safe integer; checked
484
+ * against actual bytes while reading, including non-SSE responses. */
485
+ maxResponseBytes?: number;
486
+ /** Run the operation's verification hook after success. */
487
+ verify?: boolean;
488
+ headers?: Record<string, string>;
489
+ }
490
+ export interface RenderedRequest {
491
+ method: HttpMethod;
492
+ url: string;
493
+ /** Secrets redacted as `<redacted>`. */
494
+ headers: Record<string, string>;
495
+ body: unknown;
496
+ }
497
+ /** One step of a macro preview (`ClientCore.previewMacro`). */
498
+ export interface MacroStepPreview {
499
+ /** 1-based position in the macro. */
500
+ step: number;
501
+ kind: MacroStep["kind"];
502
+ /** The step's operation id. */
503
+ operation: string;
504
+ /** The name the step's result is bound to, or null. */
505
+ as: string | null;
506
+ safety: Safety;
507
+ /** The request the step sends (a `poll` or `paginate` step's first
508
+ * request), rendered without sending anything. Arguments taken from an
509
+ * earlier step's result show as `<from step NAME: path>` (NAME is that
510
+ * step's `as` name; `<from step NAME>` for the whole result), shown as
511
+ * written in the URL, headers and body, and exempt from pre-flight
512
+ * validation. Null when the step's whole argument object comes from an
513
+ * earlier result, or a value from an earlier result would have to be
514
+ * encoded before it is known (a `bytes` or `multipart` body). */
515
+ request: RenderedRequest | null;
516
+ /** The step's own effects: its confirmation message (with the same
517
+ * placeholders), what a `poll` or `paginate` step repeats
518
+ * (`Repeats <op> every N ms until <predicate>, for at most M ms.`,
519
+ * `Reads up to N pages of <op>.`) and its remediation note. */
520
+ effects: string[];
521
+ }
522
+ export interface PreviewResult {
523
+ operation: string;
524
+ safety: Safety;
525
+ /** For a macro: the first step's request (also `steps[0].request`). */
526
+ request: RenderedRequest;
527
+ /** Side-effect summary from the manifest's confirmation message, the
528
+ * remediation note and the operation's summary. */
529
+ effects: string[];
530
+ /** Present for mutating operations; bound to the exact arguments. */
531
+ confirmation_token: string | null;
532
+ expires_in_ms: number | null;
533
+ /** For `header`/`endpoint` previews: the server's answer. */
534
+ server_preview?: unknown;
535
+ /** Macro previews only: every step's rendered request and effects, in
536
+ * order. `effects` above lists them all, each step's under a
537
+ * `Step N: <kind> <operation> (<safety>).` line. */
538
+ steps?: MacroStepPreview[];
539
+ }
540
+ export interface Page<T> {
541
+ items: T[];
542
+ /** The full page body. */
543
+ body: unknown;
544
+ /** Cursor, offset, page number or URL for the next page; null at the end. */
545
+ next: unknown;
546
+ }
547
+ /** Until-predicate for `ClientCore.poll`, same shape as `VerifyDescriptor.expect`. */
548
+ export type Predicate = Record<string, unknown>;
549
+ /** One macro step in the canonical form (`tungsten_ir::Macro.steps`).
550
+ * `args`, `budget_ms` and the macro output are expressions: strings
551
+ * starting with `$` reference `$input` or an earlier step's `as` name,
552
+ * `{expr: "<ref> in [..]" | "<ref> == x" | "<ref> != x"}` is a boolean,
553
+ * anything else a literal. */
554
+ export interface MacroStep {
555
+ kind: "call" | "poll" | "paginate";
556
+ /** Operation id, resolved among the operations registered with the core. */
557
+ operation: string;
558
+ args: unknown;
559
+ as: string | null;
560
+ /** `poll` only: predicate on the step's success body. When the budget
561
+ * runs out before it holds, the step's `as` name is bound to `null`, so
562
+ * an output like `{expr: "$status == null"}` reports the timeout. */
563
+ until: Predicate | null;
564
+ interval_ms: number | null;
565
+ budget_ms: unknown;
566
+ /** `paginate` only: page limit. */
567
+ max_pages: number | null;
568
+ }
569
+ /** A compiled macro (`tungsten_ir::Macro`).
570
+ *
571
+ * Confirmation is macro-level: when the macro's tier (the strictest of its
572
+ * declared `safety` and its steps' tiers) is `destructive` or
573
+ * `irreversible`, `runMacro` requires `confirm` like an operation of that
574
+ * tier, with a token from `previewMacro` bound to the macro name and its
575
+ * exact input. That one confirmation authorizes every step of the run;
576
+ * steps never ask for their own tokens, since their arguments may depend on
577
+ * earlier steps' results. The token is spent (bound to the run's
578
+ * idempotency key) by the first step that passes pre-flight and sends, so
579
+ * a run refused before sending anything can be corrected and repeated with
580
+ * the same token. */
581
+ export interface MacroDescriptor {
582
+ name: string;
583
+ summary: string;
584
+ safety: Safety;
585
+ steps: MacroStep[];
586
+ output: unknown;
587
+ /** The macro input: an operation's args (`extends`) plus added fields.
588
+ * An added field's JSON Schema `default` is applied when the input
589
+ * omits it. */
590
+ input: {
591
+ extends: string | null;
592
+ add: Record<string, unknown>;
593
+ };
594
+ /** Output fields carrying secrets (dotted paths); redacted in traces. */
595
+ sensitiveResponseFields?: string[];
596
+ /** The output holds values the API returns only once. */
597
+ shownOnce?: boolean;
598
+ /** Disclosure cluster (agent.yml `disclosure.clusters`). */
599
+ cluster?: string | null;
600
+ }
601
+ /** Further `ClientCore` methods beyond {@link ClientCoreApi}. */
602
+ export interface ClientCoreExtensions {
603
+ /** Make operations resolvable by id (verification, endpoint previews, macros). */
604
+ register(...ops: OperationDescriptor[]): void;
605
+ /** A registered operation by id. */
606
+ operation(id: string): OperationDescriptor | undefined;
607
+ /** Run a compiled macro; returns its output or the first failing step's
608
+ * envelope, with the completed steps' results as `partial` (a rerun is
609
+ * never offered when a completed mutating step has no replay
610
+ * protection). A `destructive` or `irreversible` macro needs
611
+ * `opts.confirm` (see {@link MacroDescriptor}). */
612
+ runMacro<T>(macro: MacroDescriptor, input: Record<string, unknown>, opts?: CallOptions): Promise<Result<T>>;
613
+ /** Preview a macro without sending anything: validates and renders
614
+ * every step's request (arguments from earlier results as
615
+ * `<from step NAME: path>` placeholders, from a dry evaluation), lists
616
+ * every step's effects and, unless the macro is read-only, returns a
617
+ * confirmation token bound to the macro and this exact input. A step
618
+ * whose arguments fail pre-flight for reasons other than a placeholder
619
+ * fails the preview, naming the step. */
620
+ previewMacro(macro: MacroDescriptor, input: Record<string, unknown>, opts?: CallOptions): Promise<Result<PreviewResult>>;
621
+ }
622
+ /** One item of a stream: an event, or the failure that ends the stream
623
+ * (an envelope; a failure before the first event is the only item). */
624
+ export type StreamItem<T> = {
625
+ ok: true;
626
+ /** The event's `data`, decoded from JSON (and validated). */
627
+ value: T;
628
+ /** The `event` field of the server-sent event, `message` when it has none. */
629
+ event: string;
630
+ /** The last event id the stream has set so far, or null. */
631
+ id: string | null;
632
+ /** The last `retry` value (milliseconds) the stream has set so far, or null. */
633
+ retry: number | null;
634
+ meta: StreamMeta;
635
+ } | {
636
+ ok: false;
637
+ error: Diagnostic;
638
+ };
639
+ /** What `ClientCore` (implemented in `client.ts`) offers generated code. */
640
+ export interface ClientCoreApi {
641
+ readonly api: ApiDescriptor;
642
+ /** Validate, authenticate, apply idempotency and confirmation rules, send
643
+ * with retries, classify the response. Never throws for API or transport
644
+ * errors; returns `{ok: false, error}`. */
645
+ call<T>(op: OperationDescriptor, args: Record<string, unknown>, opts?: CallOptions): Promise<Result<T>>;
646
+ /** Run the operation's preview mode. */
647
+ preview(op: OperationDescriptor, args: Record<string, unknown>, opts?: CallOptions): Promise<Result<PreviewResult>>;
648
+ /** Iterate the events of an operation's event stream (`text/event-stream`).
649
+ * Errors before the stream starts (validation, auth, an error status) are
650
+ * the only item, as for `call`, and are retried by the same rules. A
651
+ * started stream that drops is reconnected with `Last-Event-ID`
652
+ * (`ClientOptions.maxReconnects`) when the operation is safe to repeat;
653
+ * otherwise, or when the reconnects are used up, a connection lost or an
654
+ * idle timeout ends it with a final failure item (`TRANSPORT_FAILED`,
655
+ * `UPSTREAM_UNAVAILABLE` for a read; `OUTCOME_UNKNOWN` for a mutation;
656
+ * `code` `STREAM_IDLE` for `ClientOptions.idleTimeoutMs`), as does an event
657
+ * that is not JSON or (with `validateResponses: "strict"`) does not match
658
+ * the event type. The result is an async iterable with helpers
659
+ * (`on`, `collect`, `reduce`, `first`, `cancel`). */
660
+ stream<T>(op: OperationDescriptor, args: Record<string, unknown>, opts?: CallOptions): EventStream<T>;
661
+ /** Iterate pages; stops after the last page or the first error result. */
662
+ pages<T>(op: OperationDescriptor, args: Record<string, unknown>, opts?: CallOptions): AsyncIterable<Result<Page<T>>>;
663
+ /** Call a read operation until `until` holds or the budget runs out. */
664
+ poll<T>(op: OperationDescriptor, args: Record<string, unknown>, until: Predicate, intervalMs: number, budgetMs: number, opts?: CallOptions): Promise<Result<T> & {
665
+ timedOut?: boolean;
666
+ }>;
667
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};