@dungle-scrubs/tether-client 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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kevin Frilot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # @dungle-scrubs/tether-client
2
+
3
+ Public adapter API for external Tether participant runtimes.
4
+
5
+ Use this package from agents that live outside the Tether service. The package
6
+ owns WebSocket connection setup, replay, task claiming, claim refresh,
7
+ cancellation, task completion, task failure, and reconnect handling.
8
+
9
+ ## Agent Shape
10
+
11
+ New agents usually provide only three pieces:
12
+
13
+ - Runtime identity and session configuration for `runParticipantRuntime`.
14
+ - A list of task `workKinds` the agent is allowed to claim.
15
+ - A `ParticipantTaskExecutor` containing the domain-specific work.
16
+
17
+ ```ts
18
+ import { runParticipantRuntime, type ParticipantTaskExecutor } from "@dungle-scrubs/tether-client";
19
+
20
+ const executor: ParticipantTaskExecutor = async ({ signal, task }) => {
21
+ if (signal.aborted) {
22
+ throw new Error("Task cancelled");
23
+ }
24
+ const summary = `Handled ${task.taskId}`;
25
+ return {
26
+ output: summary,
27
+ result: { summary },
28
+ };
29
+ };
30
+
31
+ await runParticipantRuntime({
32
+ afterSeq: 0,
33
+ capabilities: { workKinds: ["example_work"] },
34
+ claimRefreshMs: 5_000,
35
+ displayName: "Example Agent",
36
+ executor,
37
+ instanceId: "inst_example",
38
+ participantId: "part_example",
39
+ runtimeKind: "example_agent",
40
+ serviceUrl: "http://127.0.0.1:3025",
41
+ sessionId: "sess_example",
42
+ workKinds: ["example_work"],
43
+ });
44
+ ```
45
+
46
+ When Tether runs with the default `AUTH_MODE=required`, pass `authToken` or set
47
+ `SERVICE_AUTH_TOKEN`. The package falls back to `TETHER_AUTH_TOKEN` only for
48
+ external consumers that need a namespaced environment variable.
49
+
50
+ The executor receives a claimed task, the active cancellation `AbortSignal`,
51
+ participant/session identity, and helper methods for extra progress or output
52
+ events. It returns the structured durable completion payload and optional
53
+ user-visible output text.
54
+
55
+ ## Runtime API Selection
56
+
57
+ Use `runParticipantRuntime` for most external agents. It connects the
58
+ participant stream, installs the default structured error channel, waits for
59
+ replay, runs optional replay-ready hooks, processes claimable tasks, refreshes
60
+ claims, and runs hook cleanup after the claim loop exits.
61
+
62
+ Use `ParticipantRuntimeClient` directly only when an adapter needs lower-level
63
+ control over participant WebSocket commands such as `appendEvent`, `claimTask`,
64
+ `refreshTaskClaim`, `completeTask`, or `failTask`.
65
+
66
+ Use `SessionEventStreamClient` for passive observers that need session replay
67
+ and live events without participant identity or task authority. Bridges,
68
+ dashboards, and approval observers should use this API when they only need
69
+ `onEvent`, `onError`, `waitForReplayComplete`, reconnect behavior, and stream
70
+ diagnostics.
@@ -0,0 +1,74 @@
1
+ import type { SessionEvent } from "./types.js";
2
+ /** Last approval processing failure captured by the observer diagnostics. */
3
+ export interface TaskApprovalObserverFailureInfo {
4
+ /** Approval id whose processing failed. */
5
+ readonly approvalId: string;
6
+ /** Original or wrapped error message. */
7
+ readonly message: string;
8
+ /** Original or wrapped error name. */
9
+ readonly name: string;
10
+ /** Observer operation that failed. */
11
+ readonly operation: string;
12
+ }
13
+ /** Snapshot of one approval observer's local process state. */
14
+ export interface TaskApprovalObserverDebugInfo {
15
+ /** Number of approval ids currently inside processApproval. */
16
+ readonly activeProcessingCount: number;
17
+ /** Current in-flight approval ids. */
18
+ readonly inFlightApprovalIds: readonly string[];
19
+ /** Most recent processing failure, if any. */
20
+ readonly lastError: TaskApprovalObserverFailureInfo | null;
21
+ /** Observer module name used for boundary logs and spans. */
22
+ readonly moduleName: string;
23
+ /** Current processed approval ids known to this process. */
24
+ readonly processedApprovalIds: readonly string[];
25
+ /** Number of replay-buffered approval ids. */
26
+ readonly replayBufferCount: number;
27
+ }
28
+ /** Callable unsubscribe handle with inspectable local observer state. */
29
+ export interface TaskApprovalObserver {
30
+ /** Stops observing approval events. */
31
+ (): void;
32
+ /** Returns a point-in-time debug snapshot of local approval observer state. */
33
+ readonly debugInfo: () => TaskApprovalObserverDebugInfo;
34
+ }
35
+ /** Error raised when a task approval processing callback rejects. */
36
+ export declare class TaskApprovalProcessingError extends Error {
37
+ /** Approval id that failed processing. */
38
+ readonly approvalId: string;
39
+ /** Observer operation that failed. */
40
+ readonly operation: string;
41
+ /** Captures approval processing context while preserving the original cause. */
42
+ constructor(input: {
43
+ readonly approvalId: string;
44
+ readonly cause: unknown;
45
+ readonly operation: string;
46
+ });
47
+ }
48
+ /** Minimal event-stream client surface needed by approval observers. */
49
+ export interface TaskApprovalObserverClient {
50
+ /** Registers one durable event handler. */
51
+ readonly onEvent: (handler: (event: SessionEvent) => void) => () => void;
52
+ /** Resolves once historical event replay has completed. */
53
+ readonly waitForReplayComplete: () => Promise<void>;
54
+ }
55
+ /** Options for observing approval events with replay-safe idempotency. */
56
+ export interface ObserveTaskApprovalsOptions<TApproval> {
57
+ /** Event stream client that provides replay and live events. */
58
+ readonly client: TaskApprovalObserverClient;
59
+ /** Stable id for one parsed approval, usually the approval event id. */
60
+ readonly approvalId: (approval: TApproval) => string;
61
+ /** Handles observer processing failures. Defaults to console.error. */
62
+ readonly onError?: (error: unknown) => void;
63
+ /** Parses a relevant approval from one durable session event. */
64
+ readonly parseApproval: (event: SessionEvent) => TApproval | null;
65
+ /** Processes one approval after replay and idempotency checks. */
66
+ readonly processApproval: (approval: TApproval) => Promise<void>;
67
+ /** Extracts already-processed approval ids from prior output events. */
68
+ readonly processedApprovalIdFromEvent: (event: SessionEvent) => string | null;
69
+ }
70
+ /**
71
+ * Observes approval events after replay while suppressing approvals that have
72
+ * already produced follow-up output.
73
+ */
74
+ export declare function observeTaskApprovals<TApproval>(options: ObserveTaskApprovalsOptions<TApproval>): TaskApprovalObserver;
@@ -0,0 +1,163 @@
1
+ import { ModuleObservability, readModuleObservabilityOptions } from "./observability.js";
2
+ /** Error raised when a task approval processing callback rejects. */
3
+ export class TaskApprovalProcessingError extends Error {
4
+ /** Approval id that failed processing. */
5
+ approvalId;
6
+ /** Observer operation that failed. */
7
+ operation;
8
+ /** Captures approval processing context while preserving the original cause. */
9
+ constructor(input) {
10
+ super(`Approval ${input.approvalId} failed during ${input.operation}`, {
11
+ cause: input.cause,
12
+ });
13
+ this.approvalId = input.approvalId;
14
+ this.name = "TaskApprovalProcessingError";
15
+ this.operation = input.operation;
16
+ }
17
+ }
18
+ /**
19
+ * Observes approval events after replay while suppressing approvals that have
20
+ * already produced follow-up output.
21
+ */
22
+ export function observeTaskApprovals(options) {
23
+ const observability = new ModuleObservability(readModuleObservabilityOptions("TaskApprovalObserver"));
24
+ const processedApprovalIds = new Set();
25
+ const inFlightApprovalIds = new Set();
26
+ const replayedApprovals = new Map();
27
+ const onError = options.onError ?? console.error;
28
+ let activeProcessingCount = 0;
29
+ let lastError = null;
30
+ let replayComplete = false;
31
+ let stopped = false;
32
+ /** Processes one approval unless it has already been handled. */
33
+ const processApproval = async (approval, eventPhase) => {
34
+ if (stopped) {
35
+ observability.debug("processApproval", "approval.skipped_stopped", {
36
+ eventPhase,
37
+ outcome: "stopped",
38
+ });
39
+ return;
40
+ }
41
+ const approvalId = options.approvalId(approval);
42
+ if (processedApprovalIds.has(approvalId)) {
43
+ observability.debug("processApproval", "approval.skipped_processed", {
44
+ approvalId,
45
+ eventPhase,
46
+ outcome: "skipped_processed",
47
+ ...stateCounts(),
48
+ });
49
+ return;
50
+ }
51
+ if (inFlightApprovalIds.has(approvalId)) {
52
+ observability.debug("processApproval", "approval.skipped_in_flight", {
53
+ approvalId,
54
+ eventPhase,
55
+ outcome: "skipped_in_flight",
56
+ ...stateCounts(),
57
+ });
58
+ return;
59
+ }
60
+ inFlightApprovalIds.add(approvalId);
61
+ activeProcessingCount += 1;
62
+ observability.debug("processApproval", "approval.started", {
63
+ approvalId,
64
+ eventPhase,
65
+ outcome: "started",
66
+ ...stateCounts(),
67
+ });
68
+ try {
69
+ await observability.traceBoundary("processApproval", { approvalId, eventPhase }, async () => {
70
+ try {
71
+ await options.processApproval(approval);
72
+ }
73
+ catch (cause) {
74
+ const error = new TaskApprovalProcessingError({
75
+ approvalId,
76
+ cause,
77
+ operation: "processApproval",
78
+ });
79
+ lastError = {
80
+ approvalId,
81
+ message: cause instanceof Error ? cause.message : String(cause),
82
+ name: cause instanceof Error ? cause.name : "UnknownError",
83
+ operation: "processApproval",
84
+ };
85
+ throw error;
86
+ }
87
+ }, () => ({ approvalId, outcome: "processed" }));
88
+ processedApprovalIds.add(approvalId);
89
+ observability.debug("processApproval", "approval.processed", {
90
+ approvalId,
91
+ eventPhase,
92
+ outcome: "processed",
93
+ ...stateCounts(),
94
+ });
95
+ }
96
+ finally {
97
+ inFlightApprovalIds.delete(approvalId);
98
+ activeProcessingCount -= 1;
99
+ }
100
+ };
101
+ const unsubscribe = options.client.onEvent((event) => {
102
+ const processedApprovalId = options.processedApprovalIdFromEvent(event);
103
+ if (processedApprovalId) {
104
+ processedApprovalIds.add(processedApprovalId);
105
+ replayedApprovals.delete(processedApprovalId);
106
+ observability.debug("onEvent", "approval.processed_output_seen", {
107
+ approvalId: processedApprovalId,
108
+ eventPhase: "processed-output",
109
+ outcome: "processed",
110
+ ...stateCounts(),
111
+ });
112
+ return;
113
+ }
114
+ const approval = options.parseApproval(event);
115
+ if (!approval) {
116
+ return;
117
+ }
118
+ if (!replayComplete) {
119
+ replayedApprovals.set(options.approvalId(approval), approval);
120
+ observability.debug("onEvent", "approval.buffered", {
121
+ approvalId: options.approvalId(approval),
122
+ eventPhase: "replay",
123
+ outcome: "buffered",
124
+ ...stateCounts(),
125
+ });
126
+ return;
127
+ }
128
+ void processApproval(approval, "live").catch(onError);
129
+ });
130
+ void options.client.waitForReplayComplete().then(() => {
131
+ if (stopped) {
132
+ return;
133
+ }
134
+ replayComplete = true;
135
+ for (const approval of replayedApprovals.values()) {
136
+ void processApproval(approval, "replay").catch(onError);
137
+ }
138
+ replayedApprovals.clear();
139
+ });
140
+ const stop = Object.assign(() => {
141
+ stopped = true;
142
+ unsubscribe();
143
+ }, {
144
+ debugInfo: () => ({
145
+ activeProcessingCount,
146
+ inFlightApprovalIds: [...inFlightApprovalIds],
147
+ lastError,
148
+ moduleName: observability.debugInfo().moduleName,
149
+ processedApprovalIds: [...processedApprovalIds],
150
+ replayBufferCount: replayedApprovals.size,
151
+ }),
152
+ });
153
+ return stop;
154
+ /** Returns local state counts for structured logs. */
155
+ function stateCounts() {
156
+ return {
157
+ activeProcessingCount,
158
+ inFlightApprovalCount: inFlightApprovalIds.size,
159
+ processedApprovalCount: processedApprovalIds.size,
160
+ replayBufferCount: replayedApprovals.size,
161
+ };
162
+ }
163
+ }
@@ -0,0 +1,6 @@
1
+ type ServiceAuthEnv = Readonly<Record<string, string | undefined>>;
2
+ /** Reads the first configured Tether service auth token from an explicit environment map. */
3
+ export declare function readServiceAuthToken(env?: ServiceAuthEnv): string | null;
4
+ /** Resolves an explicit token value, falling back to service environment names. */
5
+ export declare function resolveServiceAuthToken(authToken: string | null | undefined, env?: ServiceAuthEnv): string | null;
6
+ export {};
@@ -0,0 +1,25 @@
1
+ /** Reads the first configured Tether service auth token from an explicit environment map. */
2
+ export function readServiceAuthToken(env = readProcessEnv()) {
3
+ const serviceToken = readNonEmptyEnv(env.SERVICE_AUTH_TOKEN);
4
+ if (serviceToken) {
5
+ return serviceToken;
6
+ }
7
+ return readNonEmptyEnv(env.TETHER_AUTH_TOKEN);
8
+ }
9
+ /** Resolves an explicit token value, falling back to service environment names. */
10
+ export function resolveServiceAuthToken(authToken, env = readProcessEnv()) {
11
+ if (authToken !== undefined) {
12
+ return authToken?.trim() || null;
13
+ }
14
+ return readServiceAuthToken(env);
15
+ }
16
+ /** Reads process.env without requiring Node ambient types in downstream packages. */
17
+ function readProcessEnv() {
18
+ const maybeGlobal = globalThis;
19
+ return maybeGlobal.process?.env ?? {};
20
+ }
21
+ /** Reads a trimmed environment value, treating blanks as omitted. */
22
+ function readNonEmptyEnv(value) {
23
+ const trimmed = value?.trim();
24
+ return trimmed ? trimmed : null;
25
+ }
@@ -0,0 +1,3 @@
1
+ import { Effect } from "effect";
2
+ /** Sleeps inside an Effect program without keeping the Node.js process alive. */
3
+ export declare function sleepUnrefEffect(delayMs: number): Effect.Effect<void, never>;
@@ -0,0 +1,9 @@
1
+ import { Effect } from "effect";
2
+ /** Sleeps inside an Effect program without keeping the Node.js process alive. */
3
+ export function sleepUnrefEffect(delayMs) {
4
+ return Effect.async((resume) => {
5
+ const timeout = setTimeout(() => resume(Effect.void), delayMs);
6
+ timeout.unref();
7
+ return Effect.sync(() => clearTimeout(timeout));
8
+ });
9
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Public adapter API for external participant runtimes.
3
+ *
4
+ * Adapter implementations should import from this module instead of reaching
5
+ * into Tether service implementation paths.
6
+ */
7
+ export type { ObserveTaskApprovalsOptions, TaskApprovalObserver, TaskApprovalObserverClient, TaskApprovalObserverDebugInfo, TaskApprovalObserverFailureInfo, } from "./approval-observer.js";
8
+ export { observeTaskApprovals, TaskApprovalProcessingError } from "./approval-observer.js";
9
+ export { readServiceAuthToken, resolveServiceAuthToken } from "./auth-token.js";
10
+ export type { SessionEventStreamClientConfig, SessionEventStreamClientDebugInfo, SessionEventStreamWebSocketFactory, } from "./session-event-stream-client.js";
11
+ export { buildSessionEventStreamUrl, sessionEventObserverRuntimeKind, SessionEventStreamClient, } from "./session-event-stream-client.js";
12
+ export type { ParticipantRuntimeClientConfig, ParticipantRuntimeClientDebugInfo, ParticipantRuntimeCursorPersistPolicy, ParticipantRuntimeCursorStore, ParticipantRuntimeTaskLoopOptions, ParticipantRuntimeCleanup, ParticipantTaskExecutor, ParticipantTaskExecutorContext, ParticipantTaskExecutorResult, ParticipantTaskSelector, RunParticipantRuntimeHooks, RunParticipantRuntimeInput, } from "./participant-runtime-client.js";
13
+ export { buildParticipantRuntimeStreamUrl, ParticipantRuntimeClient, ParticipantRuntimeClientConfigurationError, ParticipantRuntimeCommandError, ParticipantRuntimeCommandTimeoutError, resolveCommandTimeoutMs, resolveResumeSeq, runParticipantRuntime, shouldClaimParticipantTask, } from "./participant-runtime-client.js";
14
+ export { buildPublishedEventInput, taskFromClaimableEvent, taskFromCreatedEvent, taskIdFromCancelledEvent, } from "./protocol.js";
15
+ export { TaskCancellationRegistry } from "./task-cancellation-registry.js";
16
+ export type { ClientSessionBindingRecord, ControlLeaseSnapshot, ControlLeaseStatus, ParticipantRuntimeKind, ParticipantRuntimeSnapshot, ParticipantRuntimeSnapshotStatus, SessionDebugSummary, SessionEvent, SessionRecord, TaskRecord, TaskSnapshot, TaskSnapshotStatus, } from "./types.js";
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Public adapter API for external participant runtimes.
3
+ *
4
+ * Adapter implementations should import from this module instead of reaching
5
+ * into Tether service implementation paths.
6
+ */
7
+ export { observeTaskApprovals, TaskApprovalProcessingError } from "./approval-observer.js";
8
+ export { readServiceAuthToken, resolveServiceAuthToken } from "./auth-token.js";
9
+ export { buildSessionEventStreamUrl, sessionEventObserverRuntimeKind, SessionEventStreamClient, } from "./session-event-stream-client.js";
10
+ export { buildParticipantRuntimeStreamUrl, ParticipantRuntimeClient, ParticipantRuntimeClientConfigurationError, ParticipantRuntimeCommandError, ParticipantRuntimeCommandTimeoutError, resolveCommandTimeoutMs, resolveResumeSeq, runParticipantRuntime, shouldClaimParticipantTask, } from "./participant-runtime-client.js";
11
+ export { buildPublishedEventInput, taskFromClaimableEvent, taskFromCreatedEvent, taskIdFromCancelledEvent, } from "./protocol.js";
12
+ export { TaskCancellationRegistry } from "./task-cancellation-registry.js";
@@ -0,0 +1,147 @@
1
+ type LogLevel = "debug" | "error" | "info" | "warn";
2
+ /**
3
+ * Snapshot of a module boundary helper's current counters and last-known
4
+ * failure state.
5
+ */
6
+ export interface BoundaryDebugInfo {
7
+ /** Number of boundary operations currently running. */
8
+ readonly activeOperations: number;
9
+ /** Total number of boundary calls observed since helper creation. */
10
+ readonly boundaryCalls: number;
11
+ /** Total number of boundary calls that ended by throwing. */
12
+ readonly boundaryFailures: number;
13
+ /** Whether structured enter, exit, and error boundary logs are enabled. */
14
+ readonly boundaryLogsEnabled: boolean;
15
+ /** Whether debug-level diagnostic logs are enabled. */
16
+ readonly debugEnabled: boolean;
17
+ /** Most recent boundary failure metadata, or null when none has failed. */
18
+ readonly lastError: StructuredErrorInfo | null;
19
+ /** Most recent operation name observed by the helper. */
20
+ readonly lastOperation: string | null;
21
+ /** Logical module name attached to logs, spans, errors, and debug snapshots. */
22
+ readonly moduleName: string;
23
+ }
24
+ /**
25
+ * Construction options for module-boundary observability helpers.
26
+ */
27
+ export interface ModuleObservabilityOptions {
28
+ /** Enables structured boundary enter, exit, and error logs when true. */
29
+ readonly boundaryLogsEnabled?: boolean;
30
+ /** Enables scoped debug logs when true. */
31
+ readonly debugEnabled?: boolean;
32
+ /** Optional structured logger; defaults to JSON console logging. */
33
+ readonly logger?: StructuredLogger;
34
+ /** Logical module name attached to logs, spans, errors, and debug snapshots. */
35
+ readonly moduleName: string;
36
+ /** Optional OpenTelemetry tracer name; defaults to the module name. */
37
+ readonly tracerName?: string;
38
+ }
39
+ /**
40
+ * Stable error metadata captured from an unknown thrown value.
41
+ */
42
+ export interface StructuredErrorInfo {
43
+ /** Human-readable error message. */
44
+ readonly message: string;
45
+ /** Error class or normalized error name. */
46
+ readonly name: string;
47
+ }
48
+ /**
49
+ * Structured module-boundary log entry emitted by ModuleObservability.
50
+ */
51
+ export interface StructuredLogEntry {
52
+ /** ISO timestamp for when the log entry was emitted. */
53
+ readonly at: string;
54
+ /** Operation-specific structured payload with sensitive data already redacted by the caller. */
55
+ readonly data: Record<string, unknown>;
56
+ /** Severity level for routing the entry to the matching log sink. */
57
+ readonly level: LogLevel;
58
+ /** Stable event name or debug message for the boundary event. */
59
+ readonly message: string;
60
+ /** Logical module name that emitted the entry. */
61
+ readonly moduleName: string;
62
+ /** Public boundary operation associated with the entry. */
63
+ readonly operation: string;
64
+ /**
65
+ * Synthetic module-boundary correlation id for pairing enter, exit, and error
66
+ * logs; this is not an OpenTelemetry trace id.
67
+ */
68
+ readonly traceId: string;
69
+ }
70
+ /**
71
+ * Sink for structured module-boundary log entries.
72
+ */
73
+ export interface StructuredLogger {
74
+ /** Receives one structured log entry. */
75
+ readonly log: (entry: StructuredLogEntry) => void;
76
+ }
77
+ /**
78
+ * Error thrown when a Tether module invariant breaks.
79
+ */
80
+ export declare class TetherInvariantError extends Error {
81
+ readonly code = "TETHER_INVARIANT_VIOLATION";
82
+ readonly details: Record<string, unknown>;
83
+ readonly moduleName: string;
84
+ readonly operation: string;
85
+ /**
86
+ * Captures a violated invariant with machine-readable module and operation
87
+ * context.
88
+ */
89
+ constructor(input: {
90
+ readonly cause?: unknown;
91
+ readonly details?: Record<string, unknown>;
92
+ readonly message: string;
93
+ readonly moduleName: string;
94
+ readonly operation: string;
95
+ });
96
+ }
97
+ /**
98
+ * JSON logger used when boundary logs are enabled through module options.
99
+ */
100
+ export declare class ConsoleStructuredLogger implements StructuredLogger {
101
+ /**
102
+ * Writes structured log entries to the matching console level.
103
+ */
104
+ log(entry: StructuredLogEntry): void;
105
+ }
106
+ /**
107
+ * Boundary observability helper for deep modules.
108
+ */
109
+ export declare class ModuleObservability {
110
+ private activeOperations;
111
+ private boundaryCalls;
112
+ private boundaryFailures;
113
+ private readonly boundaryLogsEnabled;
114
+ private readonly debugEnabled;
115
+ private lastError;
116
+ private lastOperation;
117
+ private readonly logger;
118
+ private readonly moduleName;
119
+ private readonly tracer;
120
+ /**
121
+ * Creates an observability boundary helper with optional logging and tracing
122
+ * dependencies.
123
+ */
124
+ constructor(options: ModuleObservabilityOptions);
125
+ /**
126
+ * Snapshots boundary counters and the most recent failure.
127
+ */
128
+ debugInfo(): BoundaryDebugInfo;
129
+ /**
130
+ * Runs one public boundary method inside logs and an OpenTelemetry span.
131
+ */
132
+ traceBoundary<TValue>(operation: string, input: Record<string, unknown>, action: () => Promise<TValue>, summarize?: (value: TValue) => Record<string, unknown>): Promise<TValue>;
133
+ /**
134
+ * Throws a typed invariant error when internal module assumptions break.
135
+ */
136
+ assertInvariant(condition: boolean, operation: string, message: string, details?: Record<string, unknown>): asserts condition;
137
+ /**
138
+ * Emits scoped verbose data without turning on global logging.
139
+ */
140
+ debug(operation: string, message: string, data?: Record<string, unknown>): void;
141
+ private logBoundary;
142
+ }
143
+ /**
144
+ * Reads module observability toggles from environment variables.
145
+ */
146
+ export declare function readModuleObservabilityOptions(moduleName: string, env?: NodeJS.ProcessEnv): ModuleObservabilityOptions;
147
+ export {};