@senpi/runtime 1.1.0-dev.runtime-phase-2.20260609132603 → 1.1.0-dev.runtime-phase-2.20260610130051

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/dist/actions/close-position/close-position.action.d.ts +0 -16
  2. package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
  3. package/dist/actions/close-position/close-position.action.js +38 -299
  4. package/dist/actions/close-position/close-position.action.js.map +1 -1
  5. package/dist/actions/decision-engine.d.ts +0 -2
  6. package/dist/actions/decision-engine.d.ts.map +1 -1
  7. package/dist/actions/decision-engine.js.map +1 -1
  8. package/dist/actions/llm-decision/factory.d.ts +0 -3
  9. package/dist/actions/llm-decision/factory.d.ts.map +1 -1
  10. package/dist/actions/llm-decision/factory.js +0 -1
  11. package/dist/actions/llm-decision/factory.js.map +1 -1
  12. package/dist/actions/llm-decision/openclaw-llm-decision.d.ts +0 -13
  13. package/dist/actions/llm-decision/openclaw-llm-decision.d.ts.map +1 -1
  14. package/dist/actions/llm-decision/openclaw-llm-decision.js +68 -195
  15. package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
  16. package/dist/actions/open-position/open-position.action.d.ts +0 -12
  17. package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
  18. package/dist/actions/open-position/open-position.action.js +123 -346
  19. package/dist/actions/open-position/open-position.action.js.map +1 -1
  20. package/dist/actions/position-tracker/position-tracker.action.d.ts +0 -6
  21. package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
  22. package/dist/actions/position-tracker/position-tracker.action.js +0 -48
  23. package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
  24. package/dist/dsl/events/handlers.d.ts.map +1 -1
  25. package/dist/dsl/events/handlers.js +4 -0
  26. package/dist/dsl/events/handlers.js.map +1 -1
  27. package/dist/recording/emitter.d.ts +21 -0
  28. package/dist/recording/emitter.d.ts.map +1 -0
  29. package/dist/recording/emitter.js +2 -0
  30. package/dist/recording/emitter.js.map +1 -0
  31. package/dist/recording/identity.d.ts +34 -0
  32. package/dist/recording/identity.d.ts.map +1 -0
  33. package/dist/recording/identity.js +2 -0
  34. package/dist/recording/identity.js.map +1 -0
  35. package/dist/recording/index.d.ts +11 -0
  36. package/dist/recording/index.d.ts.map +1 -0
  37. package/dist/recording/index.js +2 -0
  38. package/dist/recording/index.js.map +1 -0
  39. package/dist/recording/noop-emitter.d.ts +17 -0
  40. package/dist/recording/noop-emitter.d.ts.map +1 -0
  41. package/dist/recording/noop-emitter.js +16 -0
  42. package/dist/recording/noop-emitter.js.map +1 -0
  43. package/dist/recording/otel/attributes.d.ts +35 -0
  44. package/dist/recording/otel/attributes.d.ts.map +1 -0
  45. package/dist/recording/otel/attributes.js +131 -0
  46. package/dist/recording/otel/attributes.js.map +1 -0
  47. package/dist/recording/otel/constants.d.ts +81 -0
  48. package/dist/recording/otel/constants.d.ts.map +1 -0
  49. package/dist/recording/otel/constants.js +96 -0
  50. package/dist/recording/otel/constants.js.map +1 -0
  51. package/dist/recording/otel/ids.d.ts +14 -0
  52. package/dist/recording/otel/ids.d.ts.map +1 -0
  53. package/dist/recording/otel/ids.js +12 -0
  54. package/dist/recording/otel/ids.js.map +1 -0
  55. package/dist/recording/otel/index.d.ts +13 -0
  56. package/dist/recording/otel/index.d.ts.map +1 -0
  57. package/dist/recording/otel/index.js +9 -0
  58. package/dist/recording/otel/index.js.map +1 -0
  59. package/dist/recording/otel/mappers.d.ts +42 -0
  60. package/dist/recording/otel/mappers.d.ts.map +1 -0
  61. package/dist/recording/otel/mappers.js +337 -0
  62. package/dist/recording/otel/mappers.js.map +1 -0
  63. package/dist/recording/otel/otel-emitter.d.ts +37 -0
  64. package/dist/recording/otel/otel-emitter.d.ts.map +1 -0
  65. package/dist/recording/otel/otel-emitter.js +75 -0
  66. package/dist/recording/otel/otel-emitter.js.map +1 -0
  67. package/dist/recording/otel/redaction.d.ts +31 -0
  68. package/dist/recording/otel/redaction.d.ts.map +1 -0
  69. package/dist/recording/otel/redaction.js +40 -0
  70. package/dist/recording/otel/redaction.js.map +1 -0
  71. package/dist/recording/records.d.ts +327 -0
  72. package/dist/recording/records.d.ts.map +1 -0
  73. package/dist/recording/records.js +14 -0
  74. package/dist/recording/records.js.map +1 -0
  75. package/dist/recording/transport/batcher.d.ts +59 -0
  76. package/dist/recording/transport/batcher.d.ts.map +1 -0
  77. package/dist/recording/transport/batcher.js +234 -0
  78. package/dist/recording/transport/batcher.js.map +1 -0
  79. package/dist/recording/transport/client.d.ts +41 -0
  80. package/dist/recording/transport/client.d.ts.map +1 -0
  81. package/dist/recording/transport/client.js +69 -0
  82. package/dist/recording/transport/client.js.map +1 -0
  83. package/dist/recording/transport/config.d.ts +53 -0
  84. package/dist/recording/transport/config.d.ts.map +1 -0
  85. package/dist/recording/transport/config.js +41 -0
  86. package/dist/recording/transport/config.js.map +1 -0
  87. package/dist/recording/transport/index.d.ts +16 -0
  88. package/dist/recording/transport/index.d.ts.map +1 -0
  89. package/dist/recording/transport/index.js +12 -0
  90. package/dist/recording/transport/index.js.map +1 -0
  91. package/dist/recording/transport/rows.d.ts +88 -0
  92. package/dist/recording/transport/rows.d.ts.map +1 -0
  93. package/dist/recording/transport/rows.js +15 -0
  94. package/dist/recording/transport/rows.js.map +1 -0
  95. package/dist/recording/wiring/emitter-factory.d.ts +39 -0
  96. package/dist/recording/wiring/emitter-factory.d.ts.map +1 -0
  97. package/dist/recording/wiring/emitter-factory.js +62 -0
  98. package/dist/recording/wiring/emitter-factory.js.map +1 -0
  99. package/dist/recording/wiring/identity.d.ts +27 -0
  100. package/dist/recording/wiring/identity.d.ts.map +1 -0
  101. package/dist/recording/wiring/identity.js +51 -0
  102. package/dist/recording/wiring/identity.js.map +1 -0
  103. package/dist/recording/wiring/index.d.ts +12 -0
  104. package/dist/recording/wiring/index.d.ts.map +1 -0
  105. package/dist/recording/wiring/index.js +9 -0
  106. package/dist/recording/wiring/index.js.map +1 -0
  107. package/dist/recording/wiring/record-builders.d.ts +68 -0
  108. package/dist/recording/wiring/record-builders.d.ts.map +1 -0
  109. package/dist/recording/wiring/record-builders.js +441 -0
  110. package/dist/recording/wiring/record-builders.js.map +1 -0
  111. package/dist/recording/wiring/subscriber.d.ts +21 -0
  112. package/dist/recording/wiring/subscriber.d.ts.map +1 -0
  113. package/dist/recording/wiring/subscriber.js +171 -0
  114. package/dist/recording/wiring/subscriber.js.map +1 -0
  115. package/dist/recording/wiring/values.d.ts +19 -0
  116. package/dist/recording/wiring/values.d.ts.map +1 -0
  117. package/dist/recording/wiring/values.js +49 -0
  118. package/dist/recording/wiring/values.js.map +1 -0
  119. package/dist/runtime/create-actions.d.ts.map +1 -1
  120. package/dist/runtime/create-actions.js +0 -2
  121. package/dist/runtime/create-actions.js.map +1 -1
  122. package/dist/runtime/create-runtime-context.d.ts +0 -2
  123. package/dist/runtime/create-runtime-context.d.ts.map +1 -1
  124. package/dist/runtime/create-runtime-context.js +0 -1
  125. package/dist/runtime/create-runtime-context.js.map +1 -1
  126. package/dist/runtime/create-scanner-module.d.ts +1 -1
  127. package/dist/runtime/create-scanner-module.d.ts.map +1 -1
  128. package/dist/runtime/create-scanner-module.js +1 -2
  129. package/dist/runtime/create-scanner-module.js.map +1 -1
  130. package/dist/runtime/load-runtime-config.d.ts +9 -0
  131. package/dist/runtime/load-runtime-config.d.ts.map +1 -1
  132. package/dist/runtime/load-runtime-config.js +41 -1
  133. package/dist/runtime/load-runtime-config.js.map +1 -1
  134. package/dist/runtime/run.d.ts.map +1 -1
  135. package/dist/runtime/run.js +3 -2
  136. package/dist/runtime/run.js.map +1 -1
  137. package/dist/runtime/runtime-schema.d.ts +182 -24
  138. package/dist/runtime/runtime-schema.d.ts.map +1 -1
  139. package/dist/runtime/runtime-schema.js +17 -1
  140. package/dist/runtime/runtime-schema.js.map +1 -1
  141. package/dist/runtime/runtime.d.ts +15 -5
  142. package/dist/runtime/runtime.d.ts.map +1 -1
  143. package/dist/runtime/runtime.js +248 -273
  144. package/dist/runtime/runtime.js.map +1 -1
  145. package/dist/scanners/engine/data-providers.d.ts +0 -2
  146. package/dist/scanners/engine/data-providers.d.ts.map +1 -1
  147. package/dist/scanners/engine/data-providers.js.map +1 -1
  148. package/dist/scanners/engine/engine.d.ts.map +1 -1
  149. package/dist/scanners/engine/engine.js +9 -68
  150. package/dist/scanners/engine/engine.js.map +1 -1
  151. package/dist/scanners/events.d.ts +6 -0
  152. package/dist/scanners/events.d.ts.map +1 -1
  153. package/dist/scanners/events.js.map +1 -1
  154. package/dist/scanners/runtime-module.d.ts +0 -2
  155. package/dist/scanners/runtime-module.d.ts.map +1 -1
  156. package/dist/scanners/runtime-module.js +9 -2
  157. package/dist/scanners/runtime-module.js.map +1 -1
  158. package/dist/senpi/client.d.ts +2 -2
  159. package/dist/senpi/client.d.ts.map +1 -1
  160. package/dist/senpi/client.js +20 -120
  161. package/dist/senpi/client.js.map +1 -1
  162. package/dist/senpi/types.d.ts +1 -68
  163. package/dist/senpi/types.d.ts.map +1 -1
  164. package/dist/strategy/strategy-state.d.ts +1 -1
  165. package/dist/strategy/strategy-state.d.ts.map +1 -1
  166. package/dist/tracking/spans/plugin-span-tracker.d.ts.map +1 -1
  167. package/dist/tracking/spans/plugin-span-tracker.js +24 -4
  168. package/dist/tracking/spans/plugin-span-tracker.js.map +1 -1
  169. package/dist/tracking/spans/redact-pii.d.ts +0 -10
  170. package/dist/tracking/spans/redact-pii.d.ts.map +1 -1
  171. package/dist/tracking/spans/redact-pii.js +2 -80
  172. package/dist/tracking/spans/redact-pii.js.map +1 -1
  173. package/dist/types/runtime.d.ts +0 -3
  174. package/dist/types/runtime.d.ts.map +1 -1
  175. package/dist/types/strategy.d.ts +2 -2
  176. package/dist/types/strategy.d.ts.map +1 -1
  177. package/package.json +1 -1
  178. package/dist/actions/otel-helpers.d.ts +0 -65
  179. package/dist/actions/otel-helpers.d.ts.map +0 -1
  180. package/dist/actions/otel-helpers.js +0 -122
  181. package/dist/actions/otel-helpers.js.map +0 -1
  182. package/dist/otel/context.d.ts +0 -24
  183. package/dist/otel/context.d.ts.map +0 -1
  184. package/dist/otel/context.js +0 -29
  185. package/dist/otel/context.js.map +0 -1
  186. package/dist/otel/emitter.d.ts +0 -73
  187. package/dist/otel/emitter.d.ts.map +0 -1
  188. package/dist/otel/emitter.js +0 -248
  189. package/dist/otel/emitter.js.map +0 -1
  190. package/dist/otel/encode.d.ts +0 -48
  191. package/dist/otel/encode.d.ts.map +0 -1
  192. package/dist/otel/encode.js +0 -100
  193. package/dist/otel/encode.js.map +0 -1
  194. package/dist/otel/index.d.ts +0 -13
  195. package/dist/otel/index.d.ts.map +0 -1
  196. package/dist/otel/index.js +0 -10
  197. package/dist/otel/index.js.map +0 -1
  198. package/dist/otel/transport.d.ts +0 -57
  199. package/dist/otel/transport.d.ts.map +0 -1
  200. package/dist/otel/transport.js +0 -208
  201. package/dist/otel/transport.js.map +0 -1
  202. package/dist/otel/types.d.ts +0 -62
  203. package/dist/otel/types.d.ts.map +0 -1
  204. package/dist/otel/types.js +0 -2
  205. package/dist/otel/types.js.map +0 -1
@@ -0,0 +1,327 @@
1
+ /**
2
+ * Recording contract — the record types the runtime hands to a {@link RecordingEmitter}, covering
3
+ * domain events and runtime errors.
4
+ *
5
+ * Each record is one atomic "work unit": plain, immutable, serializable data with no methods or
6
+ * builder dance; variants are modelled with a discriminator field plus optionals. The types are
7
+ * transport-agnostic — nothing here knows about spans, attributes, or any wire format.
8
+ *
9
+ * Identity (service, recipe, runtime, strategy address, boot id) is stamped by the emitter and is
10
+ * never on a record — see {@link IdentityEnvelope}. Timestamps are epoch milliseconds; ISO strings
11
+ * from the bus are converted before they reach a record, so consumers never parse dates.
12
+ */
13
+ /**
14
+ * Trade direction as the bus reports it. The alias itself is non-null; sites that admit a
15
+ * non-directional signal widen it to `TradeDirection | null` (e.g. {@link SignalSummary}).
16
+ */
17
+ export type TradeDirection = "LONG" | "SHORT";
18
+ /**
19
+ * Lightweight projection of a scanner signal as it appears on the bus, including the explainable
20
+ * `factors`/`meta` bags so the audit trail keeps the "why". Redacting sensitive `meta` is the
21
+ * adapter's job, not this contract's.
22
+ */
23
+ export interface SignalSummary {
24
+ readonly signalType: string;
25
+ readonly asset: string;
26
+ readonly direction: TradeDirection | null;
27
+ readonly score: number;
28
+ /** Signal time, epoch ms. */
29
+ readonly timestamp: number;
30
+ readonly factors?: Record<string, boolean>;
31
+ readonly meta?: Record<string, unknown>;
32
+ }
33
+ /** Coarse terminal classification of a scanner execution. */
34
+ export type ScannerRunOutcome = "complete" | "skipped" | "error";
35
+ /**
36
+ * One scanner execution attempt (the work unit). Folds the bus's `scanner:run:start` +
37
+ * terminal `scanner:run:{complete,skip,error}` events into a single atomic record so a
38
+ * duration is always derivable (`endedAt - startedAt`). Skip/error early-exit paths can
39
+ * terminate without a paired `scanner:run:start` event; in that case Task 4 fills `startedAt`
40
+ * with the run's attempt timestamp (which equals `endedAt`), so duration is 0 rather than absent.
41
+ * The skipped/disabled distinction lives on `outcome`/`statusDetail`, not on a missing start.
42
+ */
43
+ export interface ScannerRunRecord {
44
+ readonly type: "scanner_run";
45
+ readonly scannerId: string;
46
+ readonly outcome: ScannerRunOutcome;
47
+ /** Raw engine status: `ok`|`heartbeat`|`skipped`|`halted`|`disabled`|`error`. */
48
+ readonly statusDetail?: string;
49
+ /**
50
+ * Run start, epoch ms (from `scanner:run:start`). Required: on a no-start early-exit path Task 4
51
+ * sets it to the attempt timestamp (= `endedAt`), so a `Duration` is always derivable.
52
+ */
53
+ readonly startedAt: number;
54
+ /** Run completion, epoch ms (terminal event timestamp). */
55
+ readonly endedAt: number;
56
+ readonly scannedCount?: number;
57
+ readonly actionableCount?: number;
58
+ readonly signalCount?: number;
59
+ readonly signals?: readonly SignalSummary[];
60
+ /** Per-scanner aggregated run metrics/details (`ScanResult.summary`). */
61
+ readonly summary?: Record<string, unknown>;
62
+ /** Explanation for a non-OK status (skip/error). */
63
+ readonly reason?: string;
64
+ /** Error message when `outcome === "error"`. */
65
+ readonly error?: string;
66
+ /** Error class name (`Error.name`) when `outcome === "error"` — the error-grouping key. */
67
+ readonly errorName?: string;
68
+ }
69
+ /** How the decision was produced (`emitTrackingDecision` reports this). */
70
+ export type DecisionMode = "llm" | "script" | "agent" | "rule" | "none";
71
+ /** Decision produced by the decision layer for an evaluation pass. */
72
+ export interface SignalDecision {
73
+ readonly execute: boolean;
74
+ /** Action type, e.g. `"open_position"`. */
75
+ readonly actionType: string;
76
+ /** Concrete action instance name. */
77
+ readonly actionName?: string;
78
+ /** Distinguishes a vestigial rule "decision" from a real LLM judgment. */
79
+ readonly mode?: DecisionMode;
80
+ readonly confidence: number;
81
+ /** Bar `confidence` was compared against (`min_confidence`); makes the below_confidence boundary reconstructable. */
82
+ readonly confidenceThreshold?: number;
83
+ readonly reasoning: string;
84
+ /** LLM-path auditing (from `ActionDecision.decisionTelemetry`). */
85
+ readonly model?: string;
86
+ readonly tokensUsed?: number | null;
87
+ readonly durationMs?: number;
88
+ /** Proposed per-asset overrides (leverage/margin/etc.) the decision emitted. */
89
+ readonly payload?: Record<string, unknown>;
90
+ }
91
+ /** Coarse result of acting on an evaluated signal. */
92
+ export type SignalOutcomeResult = "executed" | "rejected" | "skipped" | "below_confidence" | "error";
93
+ /**
94
+ * Outcome of acting on a **single** signal within an evaluation pass. Outcomes are per-signal: the
95
+ * action loops over the batch producing one result each and decrements margin as it goes, so a
96
+ * later signal's outcome only makes sense alongside the earlier ones.
97
+ *
98
+ * `result` is the coarse classification. `reasonCode` is an open string for the granular per-signal
99
+ * reasons (e.g. `risk_gate_COOLDOWN`, `insufficient_margin`, `below_min_notional`,
100
+ * `position_open_failed`, `no_slots`, `invalid_direction`); keeping it an open string means new
101
+ * reasons can be carried without changing this type.
102
+ */
103
+ export interface SignalOutcome {
104
+ readonly result: SignalOutcomeResult;
105
+ readonly summary?: string;
106
+ readonly reasonCode?: string;
107
+ readonly reason?: string;
108
+ readonly error?: string;
109
+ /** Error class name (`Error.name`, e.g. `"ZodError"`) — the natural error-grouping key. */
110
+ readonly errorName?: string;
111
+ readonly leverage?: number;
112
+ readonly marginAmount?: number;
113
+ readonly notionalValue?: number;
114
+ readonly orderId?: string;
115
+ readonly entryPrice?: number;
116
+ readonly size?: number;
117
+ /** Order-fill success flag from `CreatePositionOrderResult.filled` (boolean, not a quantity). */
118
+ readonly filled?: boolean | null;
119
+ /** Maker vs taker fill (`CreatePositionOrderResult.executionAsMaker`); `null` when unfilled. */
120
+ readonly executionAsMaker?: boolean | null;
121
+ readonly requiredMarginAmount?: number;
122
+ readonly availableMarginAmount?: number;
123
+ }
124
+ /**
125
+ * One evaluated signal paired with its outcome. Pairing (rather than two index-aligned arrays)
126
+ * keeps signal and outcome structurally bound, so no filter/sort/map can silently misalign them.
127
+ */
128
+ export interface SignalEvaluation {
129
+ readonly signal: SignalSummary;
130
+ readonly outcome: SignalOutcome;
131
+ }
132
+ /**
133
+ * One signal-evaluation work unit: the runtime hands a scan pass's batch to the action, which
134
+ * decides once, then acts on each signal serially. The whole pass is one record — a 3-signal batch
135
+ * is ONE record with three `evaluations`, not three records. Nesting the decision and per-signal
136
+ * outcomes keeps them bound together without a separate join key.
137
+ */
138
+ export interface SignalEvaluatedRecord {
139
+ readonly type: "signal_evaluated";
140
+ readonly scannerId?: string;
141
+ /** Evaluation start, epoch ms. Required (see {@link ScannerRunRecord.startedAt}) so a `Duration` is always derivable. */
142
+ readonly startedAt: number;
143
+ /** Evaluation end, epoch ms. */
144
+ readonly endedAt: number;
145
+ /**
146
+ * Pass-level decision (the single `ActionDecision` the engine/LLM produces for this pass).
147
+ * Absent when the pass was filtered before a decision was produced.
148
+ */
149
+ readonly decision?: SignalDecision;
150
+ /** One entry per signal in the evaluated batch; outcomes are per-signal. */
151
+ readonly evaluations: readonly SignalEvaluation[];
152
+ }
153
+ export type PositionLifecycleEvent = "opened" | "closed" | "flipped" | "increased" | "decreased" | "open_order_submitted";
154
+ /**
155
+ * A position lifecycle domain event observed on the bus. These are first-class events — they
156
+ * fire both as the outcome of acting on a signal *and* when the position-tracker scanner
157
+ * detects on-chain changes opened manually or by other tools — so they are recorded
158
+ * independently of {@link SignalEvaluatedRecord}.
159
+ *
160
+ * Variant-specific fields are optional; `event` discriminates which apply.
161
+ */
162
+ export interface PositionLifecycleRecord {
163
+ readonly type: "position_lifecycle";
164
+ readonly event: PositionLifecycleEvent;
165
+ readonly occurredAt: number;
166
+ readonly asset: string;
167
+ /** Empty string / undefined for the main book. */
168
+ readonly dex?: string;
169
+ readonly direction?: TradeDirection | null;
170
+ readonly entryPrice?: number;
171
+ readonly size?: number;
172
+ readonly leverage?: number;
173
+ readonly leverageType?: string;
174
+ readonly margin?: number;
175
+ readonly unrealizedPnl?: number;
176
+ readonly roe?: number;
177
+ /** `null` when the venue reports no liquidation price. */
178
+ readonly liquidationPrice?: number | null;
179
+ readonly reason?: string;
180
+ readonly closeReason?: string;
181
+ readonly closedPrice?: number;
182
+ readonly closedSize?: number;
183
+ /** Maker vs taker on the close (`null` when unfilled). */
184
+ readonly executionAsMaker?: boolean | null;
185
+ readonly previousEntryPrice?: number;
186
+ readonly previousSize?: number;
187
+ readonly sizeDelta?: number;
188
+ readonly newAsset?: string;
189
+ readonly orderId?: string;
190
+ readonly orderType?: string;
191
+ readonly marginAmount?: number;
192
+ /** Order-fill success flag from `CreatePositionOrderResult.filled` (boolean, not a quantity). */
193
+ readonly filled?: boolean | null;
194
+ readonly reasoning?: string;
195
+ }
196
+ export type DslTransitionKind = "created" | "phase_changed" | "tier_advanced" | "sl_updated" | "closed" | "close_pending" | "settings_updated" | "deleted";
197
+ /** A tier in the exit ladder, as carried on `dsl.created`. */
198
+ export interface DslTierSummary {
199
+ readonly triggerPct: number;
200
+ readonly lockHwPct: number;
201
+ }
202
+ /**
203
+ * A DSL exit-engine transition. One record per transition; `transition` discriminates which of the
204
+ * optional fields are populated. Periodic `dsl.heartbeat` is intentionally not modelled — it is a
205
+ * notification cadence, not a state transition.
206
+ */
207
+ export interface DslTransitionRecord {
208
+ readonly type: "dsl_transition";
209
+ readonly transition: DslTransitionKind;
210
+ readonly occurredAt: number;
211
+ readonly asset: string;
212
+ readonly dex?: string;
213
+ /** Lifecycle event that produced the DSL row (`position_opened`|`..._increased`|`flipped`). */
214
+ readonly triggerReason?: string;
215
+ readonly preset?: string;
216
+ /** Exit ladder the position was created with (`dsl.created`). */
217
+ readonly tiers?: readonly DslTierSummary[];
218
+ readonly direction?: TradeDirection;
219
+ readonly entryPrice?: number;
220
+ readonly leverage?: number;
221
+ readonly margin?: number;
222
+ readonly size?: number;
223
+ readonly floorPrice?: number;
224
+ readonly newFloorPrice?: number;
225
+ readonly phase?: 1 | 2;
226
+ /**
227
+ * Current tier index. Normalizes the three bus spellings into one field — `tierIndex`
228
+ * (`dsl.phase_changed`), `tier` (`dsl.tier_advanced`), `currentTierIndex` (`dsl.closed`) — so
229
+ * downstream never has to know which event produced the record. `null` when no tier is active.
230
+ */
231
+ readonly tierIndex?: number | null;
232
+ readonly lockHwPct?: number;
233
+ readonly triggerPct?: number;
234
+ readonly lockedProfitPct?: number;
235
+ readonly newSLPrice?: number;
236
+ readonly slOrderId?: number;
237
+ readonly reason?: string;
238
+ readonly closeReason?: string;
239
+ /** Position open time, epoch ms (on `closed`). */
240
+ readonly createdAt?: number;
241
+ readonly currentROE?: number;
242
+ readonly highWaterRoe?: number;
243
+ /** High-water *price* the trailing floor is relative to (`DslState.highWaterPrice`). */
244
+ readonly highWaterPrice?: number;
245
+ readonly peakROE?: number;
246
+ readonly lastPrice?: number;
247
+ readonly attempt?: number;
248
+ readonly updated?: readonly string[];
249
+ /**
250
+ * Senpi/MCP-side position id (`DslState.backendDslPositionId`). Captured opportunistically for
251
+ * exchange-side correlation — but **DSL-only**: the runtime must not treat this as a position
252
+ * identity, since DSL need not be active for a position to exist. `null` when not yet assigned.
253
+ */
254
+ readonly backendDslPositionId?: string | null;
255
+ }
256
+ /**
257
+ * A bucket-2 error that does not belong to a specific domain record (e.g. the bus's
258
+ * `signal_processing_error`). Errors that *do* have a home — scanner failures, action
259
+ * failures, DSL close failures — are carried on {@link ScannerRunRecord}, {@link SignalOutcome}
260
+ * and {@link DslTransitionRecord} respectively.
261
+ */
262
+ export interface RuntimeErrorRecord {
263
+ readonly type: "runtime_error";
264
+ readonly occurredAt: number;
265
+ /** Where the error originated, e.g. `"signal_processing"`. */
266
+ readonly phase: string;
267
+ readonly message: string;
268
+ /** Error class name (`Error.name`, e.g. `"ZodError"`) — the natural error-grouping key. */
269
+ readonly errorName?: string;
270
+ readonly asset?: string;
271
+ readonly scannerId?: string;
272
+ readonly stack?: string;
273
+ }
274
+ export type RuntimeLifecyclePhase = "started" | "stopped";
275
+ /**
276
+ * Runtime session boundary. Marks process start/stop and carries session shape; `strategyAddress`
277
+ * is omitted because it lives on the identity envelope.
278
+ */
279
+ export interface RuntimeLifecycleRecord {
280
+ readonly type: "runtime_lifecycle";
281
+ readonly phase: RuntimeLifecyclePhase;
282
+ readonly occurredAt: number;
283
+ readonly runtimeVersion?: string;
284
+ readonly hasDsl?: boolean;
285
+ readonly scannerCount?: number;
286
+ readonly actionCount?: number;
287
+ /**
288
+ * The **unresolved** runtime config at boot (raw YAML/config, `${VAR}` placeholders intact) — the
289
+ * retrospection snapshot of what this session ran. Deliberately NOT the resolved config: resolving
290
+ * `${VAR}` inlines secrets (e.g. `SENPI_API_KEY`), which `env-resolve.ts` forbids exporting. Keeping
291
+ * it unresolved needs no redaction and is the stabler artifact — it is also the input `configHash`
292
+ * is computed over, so blob and hash agree. See {@link IdentityEnvelope.configHash}.
293
+ */
294
+ readonly config?: Record<string, unknown>;
295
+ readonly uptimeMs?: number;
296
+ }
297
+ export type RuntimeStatePhase = "paused" | "resumed";
298
+ /**
299
+ * A runtime trading-state transition — the runtime pausing or resuming its *own* trading on a
300
+ * cap/halt. Sibling to {@link RuntimeLifecycleRecord} on a different axis: lifecycle records the
301
+ * process (started/stopped); this records whether trading is active or self-paused.
302
+ *
303
+ * `paused` corresponds to the bus's `on_strategy_self_paused` / `on_daily_limit_hit` /
304
+ * `on_drawdown_cap_hit`, which fire on a `running → paused` edge carrying the breached `metrics`.
305
+ * `resumed` is the symmetric `paused → running` re-arm; modelling it here lets the emit be added
306
+ * later without changing this type. `strategyAddress` is omitted — it lives on the identity envelope.
307
+ *
308
+ * This is the "why did the runtime stop trading" signal — for observability and for an agent
309
+ * reasoning about self-imposed halts (raise a cap, wait for daily reset, investigate drawdown).
310
+ */
311
+ export interface RuntimeStateRecord {
312
+ readonly type: "runtime_state";
313
+ readonly phase: RuntimeStatePhase;
314
+ readonly occurredAt: number;
315
+ /** Stable gate identifier (e.g. the cap/halt rule id). */
316
+ readonly gateId: string;
317
+ readonly gateName?: string;
318
+ /** Which hook produced the pause (`self_paused`|`daily_limit`|`drawdown_cap`). */
319
+ readonly source?: string;
320
+ /** Human-readable explanation with live numbers (the gate's `reason`). */
321
+ readonly reason?: string;
322
+ /** Breached-gate metrics bag (`pnlDelta`, `dailyLossLimitUsd`, `drawdownFromPeakPct`, …). */
323
+ readonly metrics?: Record<string, unknown> | null;
324
+ }
325
+ /** Discriminated union of every record type. */
326
+ export type RecordingRecord = ScannerRunRecord | SignalEvaluatedRecord | PositionLifecycleRecord | DslTransitionRecord | RuntimeErrorRecord | RuntimeLifecycleRecord | RuntimeStateRecord;
327
+ //# sourceMappingURL=records.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.d.ts","sourceRoot":"","sources":["../../src/recording/records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9C;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,cAAc,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6BAA6B;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAID,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAC;AAEjE;;;;;;;GAOG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,iFAAiF;IACjF,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC5C,yEAAyE;IACzE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC3C,oDAAoD;IACpD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,2EAA2E;AAC3E,MAAM,MAAM,YAAY,GAAG,KAAK,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAExE,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qCAAqC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,qHAAqH;IACrH,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,sDAAsD;AACtD,MAAM,MAAM,mBAAmB,GAC3B,UAAU,GACV,UAAU,GACV,SAAS,GACT,kBAAkB,GAClB,OAAO,CAAC;AAEZ;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,gGAAgG;IAChG,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE3C,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CACzC;AAED;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;CACjC;AAED;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAC;IAClC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,yHAAyH;IACzH,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gCAAgC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;OAGG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC;IACnC,4EAA4E;IAC5E,QAAQ,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,CAAC;CACnD;AAID,MAAM,MAAM,sBAAsB,GAC9B,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,WAAW,GACX,WAAW,GACX,sBAAsB,CAAC;AAE3B;;;;;;;GAOG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kDAAkD;IAClD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAEzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,0DAA0D;IAC1D,QAAQ,CAAC,gBAAgB,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IAE3C,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,iGAAiG;IACjG,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,MAAM,MAAM,iBAAiB,GACzB,SAAS,GACT,eAAe,GACf,eAAe,GACf,YAAY,GACZ,QAAQ,GACR,eAAe,GACf,kBAAkB,GAClB,SAAS,CAAC;AAEd,8DAA8D;AAC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,+FAA+F;IAC/F,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IAC3C,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAEvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAEhC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAElC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,kDAAkD;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,wFAAwF;IACxF,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAE5B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC;;;;OAIG;IACH,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/C;AAID;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,2FAA2F;IAC3F,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,SAAS,CAAC;AAE1D;;;GAGG;AACH,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,KAAK,EAAE,qBAAqB,CAAC;IACtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAEjC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE1C,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC5B;AAID,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,0EAA0E;IAC1E,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CACnD;AAID,gDAAgD;AAChD,MAAM,MAAM,eAAe,GACvB,gBAAgB,GAChB,qBAAqB,GACrB,uBAAuB,GACvB,mBAAmB,GACnB,kBAAkB,GAClB,sBAAsB,GACtB,kBAAkB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Recording contract — the record types the runtime hands to a {@link RecordingEmitter}, covering
3
+ * domain events and runtime errors.
4
+ *
5
+ * Each record is one atomic "work unit": plain, immutable, serializable data with no methods or
6
+ * builder dance; variants are modelled with a discriminator field plus optionals. The types are
7
+ * transport-agnostic — nothing here knows about spans, attributes, or any wire format.
8
+ *
9
+ * Identity (service, recipe, runtime, strategy address, boot id) is stamped by the emitter and is
10
+ * never on a record — see {@link IdentityEnvelope}. Timestamps are epoch milliseconds; ISO strings
11
+ * from the bus are converted before they reach a record, so consumers never parse dates.
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=records.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"records.js","sourceRoot":"","sources":["../../src/recording/records.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Micro-batcher — two independent buffers (traces, logs). `enqueueTrace` /
3
+ * `enqueueLog` are cheap and non-blocking; rows ship on a timer interval, with
4
+ * an early flush when a buffer nears the byte cap.
5
+ *
6
+ * Reliability policy (lean, by design):
7
+ * - **Drop over block, always.** A full buffer or a failed flush NEVER blocks
8
+ * or throws into the caller. On any error: log, count the drop, move on.
9
+ * **No retry** — the seam is left for a later dedicated stage.
10
+ * - Two distinct signals are counted: *client-dropped* (never sent — overflow,
11
+ * oversized row, an oversized batch (413), or a transport/auth/5xx flush
12
+ * failure) vs *server-rejected* (a `200` that accepted fewer rows than were
13
+ * sent, or a `400` — a malformed row that got through the mapper; a mapper
14
+ * bug to surface, not a transport failure). The 200 shortfall is derived from
15
+ * `accepted`, not the response's `rejected` field: the service reports
16
+ * schema-validation drops only in `errors[]`, so trusting `rejected` alone
17
+ * would let mapper bugs go uncounted.
18
+ * - Flush-on-interval is primary; byte-cap early-flush is the only added
19
+ * trigger. Remaining buffers flush best-effort on shutdown (time-boxed by the
20
+ * client's per-request timeout).
21
+ *
22
+ * Each row is serialized exactly once (at enqueue, for byte accounting); the
23
+ * cached JSON is reused both to size the buffer and to frame the HTTP body, so
24
+ * the cap bounds the ACTUAL serialized body (envelope + separators included).
25
+ */
26
+ import { type TelemetryClient } from "./client.js";
27
+ import type { TransportConfig } from "./config.js";
28
+ import type { LogRow, TraceRow } from "./rows.js";
29
+ /** Per-signal counters. */
30
+ export interface SignalStats {
31
+ /** Rows handed to `enqueue`. */
32
+ enqueued: number;
33
+ /** Rows the server accepted (sum of `accepted` across 2xx flushes). */
34
+ sent: number;
35
+ /** Rows never sent — overflow, oversized, or a transport/auth/5xx failure. */
36
+ clientDropped: number;
37
+ /** Rows the server refused — a 200's accepted-shortfall, or a whole 400 batch. */
38
+ serverRejected: number;
39
+ }
40
+ export interface BatcherStats {
41
+ traces: SignalStats;
42
+ logs: SignalStats;
43
+ }
44
+ export interface TelemetryBatcher {
45
+ enqueueTrace(row: TraceRow): void;
46
+ enqueueLog(row: LogRow): void;
47
+ /** Flush both buffers now (best-effort). */
48
+ flush(): Promise<void>;
49
+ /** Stop the timer and flush remaining rows (best-effort, time-boxed). */
50
+ shutdown(): Promise<void>;
51
+ /** Snapshot of per-signal counters. */
52
+ getStats(): BatcherStats;
53
+ }
54
+ /**
55
+ * Build a batcher. `client` is injectable for testing; by default it is built
56
+ * from `config`.
57
+ */
58
+ export declare function createBatcher(config: TransportConfig, client?: TelemetryClient): TelemetryBatcher;
59
+ //# sourceMappingURL=batcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batcher.d.ts","sourceRoot":"","sources":["../../../src/recording/transport/batcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAA0C,KAAK,eAAe,EAAE,MAAM,aAAa,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAKlD,2BAA2B;AAC3B,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,aAAa,EAAE,MAAM,CAAC;IACtB,kFAAkF;IAClF,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,IAAI,EAAE,WAAW,CAAC;CACnB;AA2LD,MAAM,WAAW,gBAAgB;IAC/B,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI,CAAC;IAClC,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,4CAA4C;IAC5C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,yEAAyE;IACzE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,uCAAuC;IACvC,QAAQ,IAAI,YAAY,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,eAA+C,GACtD,gBAAgB,CAkClB"}
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Micro-batcher — two independent buffers (traces, logs). `enqueueTrace` /
3
+ * `enqueueLog` are cheap and non-blocking; rows ship on a timer interval, with
4
+ * an early flush when a buffer nears the byte cap.
5
+ *
6
+ * Reliability policy (lean, by design):
7
+ * - **Drop over block, always.** A full buffer or a failed flush NEVER blocks
8
+ * or throws into the caller. On any error: log, count the drop, move on.
9
+ * **No retry** — the seam is left for a later dedicated stage.
10
+ * - Two distinct signals are counted: *client-dropped* (never sent — overflow,
11
+ * oversized row, an oversized batch (413), or a transport/auth/5xx flush
12
+ * failure) vs *server-rejected* (a `200` that accepted fewer rows than were
13
+ * sent, or a `400` — a malformed row that got through the mapper; a mapper
14
+ * bug to surface, not a transport failure). The 200 shortfall is derived from
15
+ * `accepted`, not the response's `rejected` field: the service reports
16
+ * schema-validation drops only in `errors[]`, so trusting `rejected` alone
17
+ * would let mapper bugs go uncounted.
18
+ * - Flush-on-interval is primary; byte-cap early-flush is the only added
19
+ * trigger. Remaining buffers flush best-effort on shutdown (time-boxed by the
20
+ * client's per-request timeout).
21
+ *
22
+ * Each row is serialized exactly once (at enqueue, for byte accounting); the
23
+ * cached JSON is reused both to size the buffer and to frame the HTTP body, so
24
+ * the cap bounds the ACTUAL serialized body (envelope + separators included).
25
+ */
26
+ import { createTelemetryClient } from "./client.js";
27
+ import { createLogger } from "../../utils/logger.js";
28
+ const logger = createLogger("[recording][transport][batcher]");
29
+ function newStats() {
30
+ return { enqueued: 0, sent: 0, clientDropped: 0, serverRejected: 0 };
31
+ }
32
+ /**
33
+ * One signal's buffer: holds pre-serialized rows, tracks the exact serialized
34
+ * body size, and owns its own in-flight guard so traces and logs flush
35
+ * independently.
36
+ */
37
+ class SignalBuffer {
38
+ name;
39
+ maxBatchBytes;
40
+ sendFn;
41
+ /** Pre-serialized rows (JSON), reused to frame the HTTP body. */
42
+ rows = [];
43
+ /** Σ byte length of the serialized rows (excludes envelope + separators). */
44
+ contentBytes = 0;
45
+ flushing = false;
46
+ /** The promise of the flush currently in flight, or null when idle. */
47
+ inFlight = null;
48
+ /** Fixed `{"<key>":[]}` envelope overhead, counted toward the byte cap. */
49
+ envelopeBytes;
50
+ stats = newStats();
51
+ constructor(name, maxBatchBytes, sendFn) {
52
+ this.name = name;
53
+ this.maxBatchBytes = maxBatchBytes;
54
+ this.sendFn = sendFn;
55
+ const bodyKey = name === "traces" ? "spans" : "logs";
56
+ this.envelopeBytes = Buffer.byteLength(`{"${bodyKey}":[]}`, "utf8");
57
+ }
58
+ /** Exact serialized body size for the rows currently buffered. */
59
+ bodyBytes() {
60
+ const commas = this.rows.length > 0 ? this.rows.length - 1 : 0;
61
+ return this.envelopeBytes + this.contentBytes + commas;
62
+ }
63
+ /** Would appending a `size`-byte row (plus its separator) cross the cap? */
64
+ wouldExceed(size) {
65
+ const separator = this.rows.length > 0 ? 1 : 0;
66
+ return this.bodyBytes() + separator + size > this.maxBatchBytes;
67
+ }
68
+ enqueue(row) {
69
+ this.stats.enqueued++;
70
+ const json = JSON.stringify(row);
71
+ const size = Buffer.byteLength(json, "utf8");
72
+ // A single row whose own framed body would exceed the cap can never ship.
73
+ if (this.envelopeBytes + size > this.maxBatchBytes) {
74
+ this.stats.clientDropped++;
75
+ logger.warn("dropping oversized telemetry row", {
76
+ signal: this.name,
77
+ rowBytes: size,
78
+ maxBatchBytes: this.maxBatchBytes,
79
+ });
80
+ return;
81
+ }
82
+ // Byte-cap early flush: appending this row (plus a comma separator when the
83
+ // buffer is non-empty) would cross the cap. flush() swaps the buffer out
84
+ // synchronously (before its first await), so when no flush is already in
85
+ // flight this clears room for the new row.
86
+ if (this.wouldExceed(size)) {
87
+ void this.flush();
88
+ if (this.wouldExceed(size)) {
89
+ // A flush is already in flight and the buffer is still full — drop.
90
+ this.stats.clientDropped++;
91
+ logger.warn("dropping telemetry row on buffer overflow", {
92
+ signal: this.name,
93
+ bufferBytes: this.bodyBytes(),
94
+ maxBatchBytes: this.maxBatchBytes,
95
+ });
96
+ return;
97
+ }
98
+ }
99
+ this.rows.push(json);
100
+ this.contentBytes += size;
101
+ }
102
+ flush() {
103
+ if (this.flushing || this.rows.length === 0)
104
+ return Promise.resolve();
105
+ this.flushing = true;
106
+ // Swap synchronously so enqueue sees an empty buffer immediately.
107
+ const batch = this.rows;
108
+ this.rows = [];
109
+ this.contentBytes = 0;
110
+ // Publish the in-flight promise so `drain()` (shutdown) can await it.
111
+ this.inFlight = this.send(batch).finally(() => {
112
+ this.flushing = false;
113
+ this.inFlight = null;
114
+ });
115
+ return this.inFlight;
116
+ }
117
+ /** Ship one batch and account the result. Never throws. */
118
+ async send(batch) {
119
+ try {
120
+ const result = await this.sendFn(batch);
121
+ this.account(batch.length, result);
122
+ }
123
+ catch (err) {
124
+ // The client is contracted never to throw, but the batcher is the last
125
+ // line of defense: swallow anything and count it as dropped.
126
+ this.stats.clientDropped += batch.length;
127
+ logger.warn("telemetry flush threw unexpectedly", {
128
+ signal: this.name,
129
+ batch: batch.length,
130
+ err: err instanceof Error ? err.message : String(err),
131
+ });
132
+ }
133
+ }
134
+ /**
135
+ * Best-effort shutdown drain: wait for any in-flight send to settle, then
136
+ * flush whatever accumulated while it was in flight. One extra pass suffices
137
+ * — no new rows arrive once shutdown has begun. Without this, a flush already
138
+ * in flight makes the plain `flush()` return early (the `flushing` guard),
139
+ * silently stranding the just-buffered rows.
140
+ */
141
+ async drain() {
142
+ if (this.inFlight)
143
+ await this.inFlight;
144
+ await this.flush();
145
+ }
146
+ account(batchSize, result) {
147
+ switch (result.outcome) {
148
+ case "transport":
149
+ this.stats.clientDropped += batchSize;
150
+ logger.warn("telemetry flush failed; batch dropped (no retry)", {
151
+ signal: this.name,
152
+ batch: batchSize,
153
+ status: result.status,
154
+ });
155
+ return;
156
+ case "oversized":
157
+ // 413: the body exceeded the server's limit. A client batching defect,
158
+ // not transient transport and not a per-row mapper bug — surface it
159
+ // distinctly so it isn't mistaken for a network outage.
160
+ this.stats.clientDropped += batchSize;
161
+ logger.warn("telemetry batch exceeded server size limit; dropped — lower maxBatchBytes or split", {
162
+ signal: this.name,
163
+ batch: batchSize,
164
+ status: result.status,
165
+ });
166
+ return;
167
+ case "rejected":
168
+ // 400: whole batch invalid — a mapper bug.
169
+ this.stats.serverRejected += batchSize;
170
+ logger.warn("telemetry batch server-rejected (400, all rows invalid)", {
171
+ signal: this.name,
172
+ batch: batchSize,
173
+ errors: result.errors.slice(0, 5),
174
+ });
175
+ return;
176
+ case "accepted": {
177
+ // 200: partial acceptance. Any row the server did not accept was refused
178
+ // — ownership rejections (counted in `rejected`) AND schema-validation
179
+ // drops (which the service reports only in `errors[]`, never in
180
+ // `rejected`). Derive the shortfall from `accepted` so no refused row
181
+ // goes uncounted.
182
+ this.stats.sent += result.accepted;
183
+ const refused = batchSize - result.accepted;
184
+ if (refused > 0) {
185
+ this.stats.serverRejected += refused;
186
+ // Distinguish ownership rejections (reported in `rejected`, possibly
187
+ // an ownership-service issue) from schema drops (a likely mapper bug).
188
+ const schemaDropped = Math.max(0, refused - result.rejected);
189
+ logger.warn("telemetry rows not accepted by server", {
190
+ signal: this.name,
191
+ refused,
192
+ ownershipRejected: result.rejected,
193
+ schemaDropped,
194
+ errors: result.errors.slice(0, 5),
195
+ });
196
+ }
197
+ return;
198
+ }
199
+ }
200
+ }
201
+ }
202
+ /**
203
+ * Build a batcher. `client` is injectable for testing; by default it is built
204
+ * from `config`.
205
+ */
206
+ export function createBatcher(config, client = createTelemetryClient(config)) {
207
+ const traces = new SignalBuffer("traces", config.maxBatchBytes, (rowsJson) => client.sendTraces(rowsJson));
208
+ const logs = new SignalBuffer("logs", config.maxBatchBytes, (rowsJson) => client.sendLogs(rowsJson));
209
+ const flush = async () => {
210
+ await Promise.all([traces.flush(), logs.flush()]);
211
+ };
212
+ const timer = setInterval(() => {
213
+ void flush();
214
+ }, config.flushIntervalMs);
215
+ // Telemetry flushes must not keep the process alive (Node >=22).
216
+ timer.unref();
217
+ let stopped = false;
218
+ return {
219
+ enqueueTrace: (row) => traces.enqueue(row),
220
+ enqueueLog: (row) => logs.enqueue(row),
221
+ flush,
222
+ async shutdown() {
223
+ if (stopped)
224
+ return;
225
+ stopped = true;
226
+ clearInterval(timer);
227
+ // Drain (not plain flush): wait for any in-flight send, then ship rows
228
+ // buffered during it — otherwise the `flushing` guard strands them.
229
+ await Promise.all([traces.drain(), logs.drain()]);
230
+ },
231
+ getStats: () => ({ traces: { ...traces.stats }, logs: { ...logs.stats } }),
232
+ };
233
+ }
234
+ //# sourceMappingURL=batcher.js.map