@senpi/runtime 3.0.6-dev.runtime-phase-2.20260630134410 → 3.0.6-dev.runtime-phase-2.20260630134511
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/dist/actions/{tracking-helpers.d.ts → action-events.d.ts} +24 -23
- package/dist/actions/action-events.d.ts.map +1 -0
- package/dist/actions/{tracking-helpers.js → action-events.js} +64 -82
- package/dist/actions/action-events.js.map +1 -0
- package/dist/actions/close-position/close-position.action.d.ts.map +1 -1
- package/dist/actions/close-position/close-position.action.js +8 -17
- package/dist/actions/close-position/close-position.action.js.map +1 -1
- package/dist/actions/event-derivation.d.ts +7 -6
- package/dist/actions/event-derivation.d.ts.map +1 -1
- package/dist/actions/event-derivation.js +6 -5
- package/dist/actions/event-derivation.js.map +1 -1
- package/dist/actions/llm-decision/openclaw-llm-decision.js +5 -5
- package/dist/actions/llm-decision/openclaw-llm-decision.js.map +1 -1
- package/dist/actions/open-position/open-position.action.d.ts.map +1 -1
- package/dist/actions/open-position/open-position.action.js +9 -17
- package/dist/actions/open-position/open-position.action.js.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.d.ts.map +1 -1
- package/dist/actions/position-tracker/position-tracker.action.js +1 -2
- package/dist/actions/position-tracker/position-tracker.action.js.map +1 -1
- package/dist/dsl/engine-client/event-mapping.d.ts.map +1 -1
- package/dist/dsl/engine-client/event-mapping.js +0 -3
- package/dist/dsl/engine-client/event-mapping.js.map +1 -1
- package/dist/dsl/events/build-closed-event.d.ts.map +1 -1
- package/dist/dsl/events/build-closed-event.js +0 -1
- package/dist/dsl/events/build-closed-event.js.map +1 -1
- package/dist/dsl/events/handlers.d.ts.map +1 -1
- package/dist/dsl/events/handlers.js +2 -4
- package/dist/dsl/events/handlers.js.map +1 -1
- package/dist/dsl/events/initial-state.d.ts.map +1 -1
- package/dist/dsl/events/initial-state.js +1 -0
- package/dist/dsl/events/initial-state.js.map +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts +1 -1
- package/dist/dsl/monitor/create-monitor.d.ts.map +1 -1
- package/dist/dsl/monitor/create-monitor.js +0 -30
- package/dist/dsl/monitor/create-monitor.js.map +1 -1
- package/dist/dsl/monitor/dsl-failure-events.d.ts +42 -0
- package/dist/dsl/monitor/dsl-failure-events.d.ts.map +1 -0
- package/dist/dsl/monitor/dsl-failure-events.js +73 -0
- package/dist/dsl/monitor/dsl-failure-events.js.map +1 -0
- package/dist/dsl/monitor/process-one-position.d.ts.map +1 -1
- package/dist/dsl/monitor/process-one-position.js +46 -158
- package/dist/dsl/monitor/process-one-position.js.map +1 -1
- package/dist/dsl/plugin/index.js +3 -3
- package/dist/dsl/plugin/index.js.map +1 -1
- package/dist/dsl/plugin/options.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +45 -58
- package/dist/index.js.map +1 -1
- package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts +2 -0
- package/dist/runtime/__tests__/helpers/runtime-gateway-harness.d.ts.map +1 -1
- package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js +24 -3
- package/dist/runtime/__tests__/helpers/runtime-gateway-harness.js.map +1 -1
- package/dist/runtime/create-runtime-context.d.ts +1 -1
- package/dist/runtime/create-runtime-context.d.ts.map +1 -1
- package/dist/runtime/create-scanner-module.d.ts +1 -1
- package/dist/runtime/domain-events.d.ts +4 -4
- package/dist/runtime/domain-events.d.ts.map +1 -1
- package/dist/runtime/domain-events.js +4 -4
- package/dist/runtime/domain-events.js.map +1 -1
- package/dist/runtime/lifecycle-events.d.ts +46 -3
- package/dist/runtime/lifecycle-events.d.ts.map +1 -1
- package/dist/runtime/lifecycle-events.js +66 -5
- package/dist/runtime/lifecycle-events.js.map +1 -1
- package/dist/{tracking → runtime}/position-id-registry.d.ts +14 -1
- package/dist/runtime/position-id-registry.d.ts.map +1 -0
- package/dist/{tracking → runtime}/position-id-registry.js +20 -3
- package/dist/runtime/position-id-registry.js.map +1 -0
- package/dist/runtime/run.d.ts +2 -4
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/runtime-schema.d.ts +156 -419
- package/dist/runtime/runtime-schema.d.ts.map +1 -1
- package/dist/runtime/runtime-schema.js +8 -28
- package/dist/runtime/runtime-schema.js.map +1 -1
- package/dist/runtime/runtime.d.ts +10 -9
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +116 -182
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.d.ts +3 -0
- package/dist/runtime/supervisor/long-running-supervisor.d.ts.map +1 -1
- package/dist/runtime/supervisor/long-running-supervisor.js +27 -53
- package/dist/runtime/supervisor/long-running-supervisor.js.map +1 -1
- package/dist/runtime-api/intake/intake-events.d.ts +44 -0
- package/dist/runtime-api/intake/intake-events.d.ts.map +1 -0
- package/dist/runtime-api/intake/intake-events.js +131 -0
- package/dist/runtime-api/intake/intake-events.js.map +1 -0
- package/dist/runtime-api/intake/scanner-launcher.d.ts +2 -0
- package/dist/runtime-api/intake/scanner-launcher.d.ts.map +1 -1
- package/dist/runtime-api/intake/scanner-launcher.js +58 -15
- package/dist/runtime-api/intake/scanner-launcher.js.map +1 -1
- package/dist/runtime-api/intake/supervisor-events.d.ts +61 -0
- package/dist/runtime-api/intake/supervisor-events.d.ts.map +1 -0
- package/dist/runtime-api/intake/supervisor-events.js +173 -0
- package/dist/runtime-api/intake/supervisor-events.js.map +1 -0
- package/dist/runtime-api/routes/signals.d.ts.map +1 -1
- package/dist/runtime-api/routes/signals.js +119 -27
- package/dist/runtime-api/routes/signals.js.map +1 -1
- package/dist/runtime-api/server.d.ts.map +1 -1
- package/dist/runtime-api/server.js +10 -1
- package/dist/runtime-api/server.js.map +1 -1
- package/dist/scanners/engine/data-providers.d.ts +1 -1
- package/dist/scanners/engine/engine.d.ts.map +1 -1
- package/dist/scanners/engine/engine.js +3 -0
- package/dist/scanners/engine/engine.js.map +1 -1
- package/dist/scanners/runtime-module.d.ts +1 -1
- package/dist/senpi/client.d.ts.map +1 -1
- package/dist/senpi/client.js +51 -6
- package/dist/senpi/client.js.map +1 -1
- package/dist/state/state-events.d.ts +13 -0
- package/dist/state/state-events.d.ts.map +1 -0
- package/dist/state/state-events.js +22 -0
- package/dist/state/state-events.js.map +1 -0
- package/dist/state/state-manager.d.ts.map +1 -1
- package/dist/state/state-manager.js +14 -1
- package/dist/state/state-manager.js.map +1 -1
- package/dist/telemetry/attributes.d.ts +13 -0
- package/dist/telemetry/attributes.d.ts.map +1 -0
- package/dist/telemetry/attributes.js +22 -0
- package/dist/telemetry/attributes.js.map +1 -0
- package/dist/{tracking → telemetry}/context.d.ts +6 -5
- package/dist/telemetry/context.d.ts.map +1 -0
- package/dist/{tracking → telemetry}/context.js +4 -4
- package/dist/telemetry/context.js.map +1 -0
- package/dist/{recording/records.d.ts → telemetry/event-builders.d.ts} +138 -32
- package/dist/telemetry/event-builders.d.ts.map +1 -0
- package/dist/{recording/wiring/record-builders.js → telemetry/event-builders.js} +90 -39
- package/dist/telemetry/event-builders.js.map +1 -0
- package/dist/telemetry/identity.d.ts +72 -0
- package/dist/telemetry/identity.d.ts.map +1 -0
- package/dist/{recording/wiring → telemetry}/identity.js +6 -2
- package/dist/telemetry/identity.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +137 -15
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/metrics.js +357 -74
- package/dist/telemetry/metrics.js.map +1 -1
- package/dist/telemetry/otel-sdk.d.ts +1 -1
- package/dist/telemetry/otel-sdk.js +1 -1
- package/dist/telemetry/spans/constants.d.ts.map +1 -0
- package/dist/telemetry/spans/constants.js.map +1 -0
- package/dist/{tracking → telemetry}/spans/gen-ai.d.ts +4 -0
- package/dist/telemetry/spans/gen-ai.d.ts.map +1 -0
- package/dist/{tracking → telemetry}/spans/gen-ai.js +9 -0
- package/dist/telemetry/spans/gen-ai.js.map +1 -0
- package/dist/telemetry/spans/index.d.ts.map +1 -0
- package/dist/telemetry/spans/index.js.map +1 -0
- package/dist/telemetry/spans/redact-pii.d.ts.map +1 -0
- package/dist/{tracking → telemetry}/spans/redact-pii.js +4 -1
- package/dist/telemetry/spans/redact-pii.js.map +1 -0
- package/dist/telemetry/spans/sanitize-payload.d.ts.map +1 -0
- package/dist/telemetry/spans/sanitize-payload.js.map +1 -0
- package/dist/telemetry/spans/span-hooks.d.ts +18 -0
- package/dist/telemetry/spans/span-hooks.d.ts.map +1 -0
- package/dist/{tracking → telemetry}/spans/span-hooks.js +183 -28
- package/dist/telemetry/spans/span-hooks.js.map +1 -0
- package/dist/telemetry/tool-plane.d.ts +31 -0
- package/dist/telemetry/tool-plane.d.ts.map +1 -0
- package/dist/telemetry/tool-plane.js +70 -0
- package/dist/telemetry/tool-plane.js.map +1 -0
- package/dist/telemetry/trace-identity.d.ts +1 -1
- package/dist/telemetry/trace-identity.d.ts.map +1 -1
- package/dist/telemetry/trace-identity.js +3 -0
- package/dist/telemetry/trace-identity.js.map +1 -1
- package/dist/types/dsl/index.d.ts +8 -9
- package/dist/types/dsl/index.d.ts.map +1 -1
- package/dist/types/runtime.d.ts +1 -1
- package/dist/types/runtime.d.ts.map +1 -1
- package/dist/utils/event-catalog.d.ts +239 -2
- package/dist/utils/event-catalog.d.ts.map +1 -1
- package/dist/utils/event-catalog.js +53 -2
- package/dist/utils/event-catalog.js.map +1 -1
- package/dist/utils/logger.d.ts +5 -0
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +21 -12
- package/dist/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/dist/actions/tracking-helpers.d.ts.map +0 -1
- package/dist/actions/tracking-helpers.js.map +0 -1
- package/dist/recording/emitter.d.ts +0 -27
- package/dist/recording/emitter.d.ts.map +0 -1
- package/dist/recording/emitter.js +0 -2
- package/dist/recording/emitter.js.map +0 -1
- package/dist/recording/identity.d.ts +0 -34
- package/dist/recording/identity.d.ts.map +0 -1
- package/dist/recording/identity.js +0 -2
- package/dist/recording/identity.js.map +0 -1
- package/dist/recording/index.d.ts +0 -11
- package/dist/recording/index.d.ts.map +0 -1
- package/dist/recording/index.js +0 -2
- package/dist/recording/index.js.map +0 -1
- package/dist/recording/noop-emitter.d.ts +0 -23
- package/dist/recording/noop-emitter.d.ts.map +0 -1
- package/dist/recording/noop-emitter.js +0 -22
- package/dist/recording/noop-emitter.js.map +0 -1
- package/dist/recording/otel/attributes.d.ts +0 -37
- package/dist/recording/otel/attributes.d.ts.map +0 -1
- package/dist/recording/otel/attributes.js +0 -131
- package/dist/recording/otel/attributes.js.map +0 -1
- package/dist/recording/otel/constants.d.ts +0 -123
- package/dist/recording/otel/constants.d.ts.map +0 -1
- package/dist/recording/otel/constants.js +0 -172
- package/dist/recording/otel/constants.js.map +0 -1
- package/dist/recording/otel/ids.d.ts +0 -25
- package/dist/recording/otel/ids.d.ts.map +0 -1
- package/dist/recording/otel/ids.js +0 -37
- package/dist/recording/otel/ids.js.map +0 -1
- package/dist/recording/otel/index.d.ts +0 -13
- package/dist/recording/otel/index.d.ts.map +0 -1
- package/dist/recording/otel/index.js +0 -9
- package/dist/recording/otel/index.js.map +0 -1
- package/dist/recording/otel/mappers.d.ts +0 -100
- package/dist/recording/otel/mappers.d.ts.map +0 -1
- package/dist/recording/otel/mappers.js +0 -684
- package/dist/recording/otel/mappers.js.map +0 -1
- package/dist/recording/otel/otel-emitter.d.ts +0 -43
- package/dist/recording/otel/otel-emitter.d.ts.map +0 -1
- package/dist/recording/otel/otel-emitter.js +0 -99
- package/dist/recording/otel/otel-emitter.js.map +0 -1
- package/dist/recording/otel/redaction.d.ts +0 -47
- package/dist/recording/otel/redaction.d.ts.map +0 -1
- package/dist/recording/otel/redaction.js +0 -88
- package/dist/recording/otel/redaction.js.map +0 -1
- package/dist/recording/records.d.ts.map +0 -1
- package/dist/recording/records.js +0 -14
- package/dist/recording/records.js.map +0 -1
- package/dist/recording/transport/batcher.d.ts +0 -59
- package/dist/recording/transport/batcher.d.ts.map +0 -1
- package/dist/recording/transport/batcher.js +0 -234
- package/dist/recording/transport/batcher.js.map +0 -1
- package/dist/recording/transport/client.d.ts +0 -41
- package/dist/recording/transport/client.d.ts.map +0 -1
- package/dist/recording/transport/client.js +0 -69
- package/dist/recording/transport/client.js.map +0 -1
- package/dist/recording/transport/config.d.ts +0 -53
- package/dist/recording/transport/config.d.ts.map +0 -1
- package/dist/recording/transport/config.js +0 -41
- package/dist/recording/transport/config.js.map +0 -1
- package/dist/recording/transport/index.d.ts +0 -16
- package/dist/recording/transport/index.d.ts.map +0 -1
- package/dist/recording/transport/index.js +0 -12
- package/dist/recording/transport/index.js.map +0 -1
- package/dist/recording/transport/rows.d.ts +0 -88
- package/dist/recording/transport/rows.d.ts.map +0 -1
- package/dist/recording/transport/rows.js +0 -15
- package/dist/recording/transport/rows.js.map +0 -1
- package/dist/recording/wiring/emitter-factory.d.ts +0 -34
- package/dist/recording/wiring/emitter-factory.d.ts.map +0 -1
- package/dist/recording/wiring/emitter-factory.js +0 -44
- package/dist/recording/wiring/emitter-factory.js.map +0 -1
- package/dist/recording/wiring/identity.d.ts +0 -27
- package/dist/recording/wiring/identity.d.ts.map +0 -1
- package/dist/recording/wiring/identity.js.map +0 -1
- package/dist/recording/wiring/index.d.ts +0 -12
- package/dist/recording/wiring/index.d.ts.map +0 -1
- package/dist/recording/wiring/index.js +0 -9
- package/dist/recording/wiring/index.js.map +0 -1
- package/dist/recording/wiring/record-builders.d.ts +0 -107
- package/dist/recording/wiring/record-builders.d.ts.map +0 -1
- package/dist/recording/wiring/record-builders.js.map +0 -1
- package/dist/recording/wiring/subscriber.d.ts +0 -28
- package/dist/recording/wiring/subscriber.d.ts.map +0 -1
- package/dist/recording/wiring/subscriber.js +0 -173
- package/dist/recording/wiring/subscriber.js.map +0 -1
- package/dist/recording/wiring/values.d.ts +0 -19
- package/dist/recording/wiring/values.d.ts.map +0 -1
- package/dist/recording/wiring/values.js +0 -49
- package/dist/recording/wiring/values.js.map +0 -1
- package/dist/tracking/build-rehydrate-items.d.ts +0 -15
- package/dist/tracking/build-rehydrate-items.d.ts.map +0 -1
- package/dist/tracking/build-rehydrate-items.js +0 -19
- package/dist/tracking/build-rehydrate-items.js.map +0 -1
- package/dist/tracking/constants.d.ts +0 -24
- package/dist/tracking/constants.d.ts.map +0 -1
- package/dist/tracking/constants.js +0 -52
- package/dist/tracking/constants.js.map +0 -1
- package/dist/tracking/context.d.ts.map +0 -1
- package/dist/tracking/context.js.map +0 -1
- package/dist/tracking/index.d.ts +0 -16
- package/dist/tracking/index.d.ts.map +0 -1
- package/dist/tracking/index.js +0 -11
- package/dist/tracking/index.js.map +0 -1
- package/dist/tracking/plugin-event-tracker.d.ts +0 -13
- package/dist/tracking/plugin-event-tracker.d.ts.map +0 -1
- package/dist/tracking/plugin-event-tracker.js +0 -27
- package/dist/tracking/plugin-event-tracker.js.map +0 -1
- package/dist/tracking/position-id-registry.d.ts.map +0 -1
- package/dist/tracking/position-id-registry.js.map +0 -1
- package/dist/tracking/spans/constants.d.ts.map +0 -1
- package/dist/tracking/spans/constants.js.map +0 -1
- package/dist/tracking/spans/gen-ai.d.ts.map +0 -1
- package/dist/tracking/spans/gen-ai.js.map +0 -1
- package/dist/tracking/spans/index.d.ts.map +0 -1
- package/dist/tracking/spans/index.js.map +0 -1
- package/dist/tracking/spans/redact-pii.d.ts.map +0 -1
- package/dist/tracking/spans/redact-pii.js.map +0 -1
- package/dist/tracking/spans/sanitize-payload.d.ts.map +0 -1
- package/dist/tracking/spans/sanitize-payload.js.map +0 -1
- package/dist/tracking/spans/span-hooks.d.ts +0 -11
- package/dist/tracking/spans/span-hooks.d.ts.map +0 -1
- package/dist/tracking/spans/span-hooks.js.map +0 -1
- package/dist/tracking/tracker.d.ts +0 -30
- package/dist/tracking/tracker.d.ts.map +0 -1
- package/dist/tracking/tracker.js +0 -66
- package/dist/tracking/tracker.js.map +0 -1
- package/dist/tracking/tracking-handler.d.ts +0 -12
- package/dist/tracking/tracking-handler.d.ts.map +0 -1
- package/dist/tracking/tracking-handler.js +0 -148
- package/dist/tracking/tracking-handler.js.map +0 -1
- package/dist/tracking/transport.d.ts +0 -35
- package/dist/tracking/transport.d.ts.map +0 -1
- package/dist/tracking/transport.js +0 -133
- package/dist/tracking/transport.js.map +0 -1
- package/dist/tracking/types.d.ts +0 -21
- package/dist/tracking/types.d.ts.map +0 -1
- package/dist/tracking/types.js +0 -6
- package/dist/tracking/types.js.map +0 -1
- /package/dist/{tracking → telemetry}/spans/constants.d.ts +0 -0
- /package/dist/{tracking → telemetry}/spans/constants.js +0 -0
- /package/dist/{tracking → telemetry}/spans/index.d.ts +0 -0
- /package/dist/{tracking → telemetry}/spans/index.js +0 -0
- /package/dist/{tracking → telemetry}/spans/redact-pii.d.ts +0 -0
- /package/dist/{tracking → telemetry}/spans/sanitize-payload.d.ts +0 -0
- /package/dist/{tracking → telemetry}/spans/sanitize-payload.js +0 -0
package/dist/runtime/runtime.js
CHANGED
|
@@ -31,22 +31,32 @@ import { createActionNotificationSubscriber, isActionLifecycleNotificationsEnabl
|
|
|
31
31
|
import { MaxLeverageCache } from "../leverage/max-leverage-cache.js";
|
|
32
32
|
import { buildStateLayout } from "./state-layout.js";
|
|
33
33
|
import { createLogger } from "../utils/logger.js";
|
|
34
|
-
import { buildScannerSummaryEvent, buildRuntimeStartedEvent, buildRuntimeStoppedEvent, buildRuntimeErrorEvent, } from "./lifecycle-events.js";
|
|
34
|
+
import { buildScannerSummaryEvent, buildRuntimeStartedEvent, buildRuntimeStoppedEvent, buildRuntimeErrorEvent, buildQueueBackpressureEvent, } from "./lifecycle-events.js";
|
|
35
35
|
import { emitEvent } from "../actions/event-derivation.js";
|
|
36
36
|
import { createDomainEventSubscriber } from "./domain-events.js";
|
|
37
|
-
import {
|
|
37
|
+
import { PositionIdRegistry, buildRehydrateItems, } from "./position-id-registry.js";
|
|
38
38
|
import { VERSION } from "../version.js";
|
|
39
39
|
import { randomUUID } from "node:crypto";
|
|
40
|
-
import { trackingContext } from "../
|
|
41
|
-
import { buildIdentity,
|
|
40
|
+
import { trackingContext } from "../telemetry/context.js";
|
|
41
|
+
import { buildIdentity, } from "../telemetry/identity.js";
|
|
42
42
|
import { runReconcile } from "./reconcile-run.js";
|
|
43
43
|
import { KeyedMutex } from "../utils/keyed-mutex.js";
|
|
44
44
|
import { withSpan } from "../telemetry/tracing.js";
|
|
45
|
+
import { registerDslActivePositionsGauge, registerQueueDepthGauge, recordScannerRun, recordSignalQueueWait, recordRuntimeHeartbeat, } from "../telemetry/metrics.js";
|
|
45
46
|
import { isOtlpEndpointConfigured } from "../telemetry/otel-sdk.js";
|
|
46
47
|
import { identityTraceAttributes, setTraceIdentityAttributes, } from "../telemetry/trace-identity.js";
|
|
47
48
|
const logger = createLogger("[senpi-core][runtime]");
|
|
48
49
|
/** Fixed retry interval for the boot-reconcile loop. */
|
|
49
50
|
const RECONCILE_RETRY_INTERVAL_MS = 15_000;
|
|
51
|
+
/**
|
|
52
|
+
* Signal-queue depth at which the serial consumer is judged to be falling behind. It processes one
|
|
53
|
+
* signal at a time (each may run an LLM decision), so a backlog this deep means inflow is outpacing
|
|
54
|
+
* processing — a `runtime.queue_backpressure` alert. Hardcoded: the depth a stall shows at is a
|
|
55
|
+
* runtime invariant, not a per-recipe tuning knob.
|
|
56
|
+
*/
|
|
57
|
+
const SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD = 10;
|
|
58
|
+
/** Default liveness-heartbeat window (~15 min) when `health.window_seconds` is unset. */
|
|
59
|
+
const DEFAULT_LIVENESS_WINDOW_SECONDS = 900;
|
|
50
60
|
export class Runtime {
|
|
51
61
|
config;
|
|
52
62
|
options;
|
|
@@ -59,10 +69,7 @@ export class Runtime {
|
|
|
59
69
|
// riskGuardian removed — gate decisions are now real-time in checkGate
|
|
60
70
|
senpi = null;
|
|
61
71
|
healthStateModule = null;
|
|
62
|
-
trackingHandler = null;
|
|
63
|
-
recordingSubscriber = null;
|
|
64
72
|
domainEventSubscriber = null;
|
|
65
|
-
recordingBatcher = null;
|
|
66
73
|
/** Lazy runtime identity builder; stamps per-trace identity at the signal.process seam. */
|
|
67
74
|
buildTraceIdentity;
|
|
68
75
|
traceIdentity;
|
|
@@ -74,13 +81,17 @@ export class Runtime {
|
|
|
74
81
|
signalQueueWait = null;
|
|
75
82
|
signalConsumerStopped = false;
|
|
76
83
|
signalConsumerDone = null;
|
|
84
|
+
/** Disposer for this runtime's `runtime.queue_depth` gauge observation; set on start, called on stop. */
|
|
85
|
+
queueDepthGaugeDispose = null;
|
|
86
|
+
/** Disposer for this runtime's `dsl.active_positions` gauge observation; set on start, called on stop. */
|
|
87
|
+
dslActivePositionsGaugeDispose = null;
|
|
88
|
+
/** Interval emitting this runtime's liveness heartbeat; set on start, cleared on stop. */
|
|
89
|
+
livenessHeartbeatTimer = null;
|
|
77
90
|
runActionsForSignal;
|
|
78
91
|
strategyConfig;
|
|
79
92
|
actionRuntimeModule;
|
|
80
93
|
/** Stored so we can call off() in stop() and avoid retaining Runtime via scanner EventEmitter. */
|
|
81
94
|
onScannerRunComplete = null;
|
|
82
|
-
onScannerRunSkip = null;
|
|
83
|
-
onScannerRunError = null;
|
|
84
95
|
/** Cached strategy id resolved from the runtime wallet on first {@link auditQuery} call. */
|
|
85
96
|
cachedStrategyId = null;
|
|
86
97
|
/** In-flight resolution promise; ensures concurrent first-burst callers reuse one `listStrategies`. */
|
|
@@ -307,18 +318,6 @@ export class Runtime {
|
|
|
307
318
|
validateDslPositionTracking(this.config, hasDsl);
|
|
308
319
|
this.bus = createRuntimeBus();
|
|
309
320
|
this.hookSystem = createHookSystem(this.bus);
|
|
310
|
-
// Event tracking: passive bus → EventTracker → BatchingTransport POST (requires JWT-capable API key).
|
|
311
|
-
// Without SENPI_API_KEY, NoOpTransport discards all events; warn once at startup.
|
|
312
|
-
const trackingApiKey = apiKey ?? process.env.SENPI_AUTH_TOKEN ?? process.env.SENPI_API_KEY;
|
|
313
|
-
const trackingBaseUrl = process.env.SENPI_TRACKING_URL ?? TRACKING_API_BASE_URL;
|
|
314
|
-
const trackingTransport = trackingApiKey
|
|
315
|
-
? new BatchingTransport({ baseUrl: trackingBaseUrl, apiKey: trackingApiKey })
|
|
316
|
-
: (() => {
|
|
317
|
-
logger.warn("event tracking disabled: set SENPI_API_KEY (or pass apiKey) so events reach senpi-agent-telemetry; without it NoOpTransport drops all telemetry");
|
|
318
|
-
return new NoOpTransport();
|
|
319
|
-
})();
|
|
320
|
-
const tracker = new EventTracker(trackingTransport, this.config.name, VERSION, this.config.strategy?.wallet ?? "");
|
|
321
|
-
this.trackingHandler = createTrackingHandler(this.bus, tracker);
|
|
322
321
|
this.stateManager = new StateManager(stateDir);
|
|
323
322
|
await this.stateManager.hydrate();
|
|
324
323
|
this.positionIdRegistry = new PositionIdRegistry();
|
|
@@ -342,15 +341,8 @@ export class Runtime {
|
|
|
342
341
|
this.senpi = senpi;
|
|
343
342
|
this.strategyConfig = resolveStrategyConfig(this.config);
|
|
344
343
|
await this.rehydratePositionIdRegistry();
|
|
345
|
-
//
|
|
346
|
-
//
|
|
347
|
-
// fingerprint are built lazily from the unresolved config — only on the live path, and never
|
|
348
|
-
// from the resolved config (which would inline secrets).
|
|
349
|
-
const telemetryConfig = this.config.telemetry;
|
|
350
|
-
// The OTLP exporter env is the single telemetry master switch: the OTel SDK (stood
|
|
351
|
-
// up at plugin register()) and this legacy recording pipeline both gate on it. With
|
|
352
|
-
// it set, recording is on (degrading to a noop emitter when no token is present);
|
|
353
|
-
// without it, both are off.
|
|
344
|
+
// The OTLP exporter env is the telemetry master switch: the OTel SDK (stood up at plugin
|
|
345
|
+
// register()) and the domain-event log both gate on it.
|
|
354
346
|
const telemetryEnabled = isOtlpEndpointConfigured();
|
|
355
347
|
// Lazy identity builder. Built only on the live path (never from the resolved
|
|
356
348
|
// config, which would inline secrets).
|
|
@@ -358,42 +350,20 @@ export class Runtime {
|
|
|
358
350
|
name: this.config.name,
|
|
359
351
|
recipeVersion: this.config.version,
|
|
360
352
|
strategyAddress: this.strategyConfig.address,
|
|
353
|
+
group: this.config.group,
|
|
361
354
|
bootId: this.runtimeBootId,
|
|
362
355
|
unresolvedConfig: this.options.unresolvedConfig,
|
|
363
356
|
});
|
|
364
357
|
// Share the builder with the signal.process root span (resolved lazily, once per boot).
|
|
365
358
|
this.buildTraceIdentity = buildIdentityEnvelope;
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
token: trackingApiKey,
|
|
369
|
-
identity: buildIdentityEnvelope,
|
|
370
|
-
scopeVersion: VERSION,
|
|
371
|
-
baseUrl: telemetryConfig?.url,
|
|
372
|
-
// v2.0.0: recipe carries flush_interval_seconds; the transport expects ms.
|
|
373
|
-
flushIntervalMs: typeof telemetryConfig?.flush_interval_seconds === "number"
|
|
374
|
-
? telemetryConfig.flush_interval_seconds * 1000
|
|
375
|
-
: undefined,
|
|
376
|
-
});
|
|
377
|
-
this.recordingBatcher = recording.batcher;
|
|
378
|
-
// Only attach the subscriber when telemetry is live (batcher present): with a noop emitter,
|
|
379
|
-
// per-event record assembly on the serial signal path would be pure dead work.
|
|
380
|
-
if (recording.batcher) {
|
|
381
|
-
this.recordingSubscriber = createRecordingSubscriber(this.bus, recording.emitter, {
|
|
382
|
-
unresolvedConfig: this.options.unresolvedConfig,
|
|
383
|
-
// Narrow read-only accessor over the registry — recording never sees the
|
|
384
|
-
// registry or RuntimeContext; sync map read, safe on the bus handler path.
|
|
385
|
-
resolvePositionId: (address, dex, asset) => this.positionIdRegistry.get(positionRegistryKey(address, dex, asset))?.positionId,
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
// The event-log half of the real domain events (position.*/dsl.*) → OTel Logs API. Gated on the
|
|
389
|
-
// enable flag (not the recording batcher): it rides the OTel SDK's log provider, independent of
|
|
390
|
-
// the custom recording pipeline. Per-event assembly on the serial signal path is dead work when
|
|
359
|
+
// The event-log half of the real domain events (position.*/dsl.*) → OTel Logs API. Rides the
|
|
360
|
+
// OTel SDK's log provider. Per-event assembly on the serial signal path is dead work when
|
|
391
361
|
// telemetry is off, so only attach when enabled.
|
|
392
362
|
if (telemetryEnabled) {
|
|
393
363
|
this.domainEventSubscriber = createDomainEventSubscriber(this.bus);
|
|
394
364
|
}
|
|
395
365
|
// Everything from here to `started = true` can throw; without the catch, stop() would
|
|
396
|
-
// early-return on `!this.started` and the
|
|
366
|
+
// early-return on `!this.started` and the domain-event subscriber would leak.
|
|
397
367
|
try {
|
|
398
368
|
// try {
|
|
399
369
|
// const { reconcileStateWithBackend } = await import("./reconcile-state.js");
|
|
@@ -545,32 +515,40 @@ export class Runtime {
|
|
|
545
515
|
},
|
|
546
516
|
});
|
|
547
517
|
this.onScannerRunComplete = (event) => {
|
|
548
|
-
|
|
549
|
-
//
|
|
550
|
-
//
|
|
551
|
-
// the id minted at the scanner.run span so scan + pass share one senpi.run.id.
|
|
518
|
+
// Per-scan-run id: stamped on the scanner_run emit and echoed through the pass so scan +
|
|
519
|
+
// pass land in one trace. Prefer the id minted at the scanner.run span so scan + pass
|
|
520
|
+
// share one senpi.run.id.
|
|
552
521
|
const runId = event.result.runId ?? randomUUID();
|
|
553
|
-
this.signalQueue.push({ result: event.result,
|
|
522
|
+
this.signalQueue.push({ result: event.result, enqueuedAt: Date.now(), runId });
|
|
523
|
+
// Edge-triggered backpressure alarm: depth rises one enqueue at a time, so `=== threshold`
|
|
524
|
+
// fires exactly once as it crosses up and re-arms only after the consumer drains back below.
|
|
525
|
+
if (this.signalQueue.length === SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD) {
|
|
526
|
+
emitEvent(logger, () => buildQueueBackpressureEvent({
|
|
527
|
+
depth: this.signalQueue.length,
|
|
528
|
+
threshold: SIGNAL_QUEUE_BACKPRESSURE_THRESHOLD,
|
|
529
|
+
scannerId: event.result.scannerId,
|
|
530
|
+
address: this.strategyConfig.address,
|
|
531
|
+
}));
|
|
532
|
+
}
|
|
554
533
|
if (this.signalQueueWait) {
|
|
555
534
|
const w = this.signalQueueWait;
|
|
556
535
|
this.signalQueueWait = null;
|
|
557
536
|
w();
|
|
558
537
|
}
|
|
559
|
-
//
|
|
538
|
+
// Scanner-run summary payload (startedAt is data stamping for telemetry durations).
|
|
560
539
|
const scannerRunPayload = {
|
|
561
540
|
address: event.result.address,
|
|
562
541
|
data: {
|
|
563
542
|
...event.result,
|
|
564
543
|
startedAt: event.startedAt,
|
|
565
544
|
runId,
|
|
566
|
-
__correlationId: correlationId,
|
|
567
545
|
},
|
|
568
546
|
timestamp: new Date().toISOString(),
|
|
569
547
|
};
|
|
570
|
-
|
|
571
|
-
//
|
|
572
|
-
// guard — telemetry never disturbs the scan path.
|
|
548
|
+
// First-class event log. The build runs inside emitEvent's guard — telemetry never
|
|
549
|
+
// disturbs the scan path.
|
|
573
550
|
emitEvent(logger, () => buildScannerSummaryEvent(scannerRunPayload, Date.now()));
|
|
551
|
+
recordScannerRun({ scannerId: event.result.scannerId, outcome: event.result.status });
|
|
574
552
|
// Forward scanner summary alerts (e.g. watchdog) to notification service
|
|
575
553
|
const alerts = event.result.summary?.alerts;
|
|
576
554
|
if (Array.isArray(alerts) && alerts.length > 0) {
|
|
@@ -580,37 +558,6 @@ export class Runtime {
|
|
|
580
558
|
}
|
|
581
559
|
};
|
|
582
560
|
this.scannerModule.on("scanner:run:complete", this.onScannerRunComplete);
|
|
583
|
-
// Surface skipped/errored scanner runs on the bus for telemetry (add-only observation events —
|
|
584
|
-
// distinct names so existing `tracking.scanner_run` consumers see an unchanged payload mix).
|
|
585
|
-
// Neither enqueues a signal: only completed runs feed the signal queue.
|
|
586
|
-
this.onScannerRunSkip = (event) => {
|
|
587
|
-
this.bus.emit("tracking.scanner_run_skipped", {
|
|
588
|
-
address: event.address,
|
|
589
|
-
data: {
|
|
590
|
-
scannerId: event.scannerId,
|
|
591
|
-
status: event.status,
|
|
592
|
-
reason: event.reason,
|
|
593
|
-
timestamp: event.timestamp,
|
|
594
|
-
startedAt: event.startedAt,
|
|
595
|
-
},
|
|
596
|
-
timestamp: new Date().toISOString(),
|
|
597
|
-
});
|
|
598
|
-
};
|
|
599
|
-
this.onScannerRunError = (event) => {
|
|
600
|
-
this.bus.emit("tracking.scanner_run_error", {
|
|
601
|
-
address: event.address,
|
|
602
|
-
data: {
|
|
603
|
-
scannerId: event.scannerId,
|
|
604
|
-
status: "error",
|
|
605
|
-
reason: typeof event.error === "string" ? event.error : String(event.error),
|
|
606
|
-
timestamp: event.timestamp,
|
|
607
|
-
startedAt: event.startedAt,
|
|
608
|
-
},
|
|
609
|
-
timestamp: new Date().toISOString(),
|
|
610
|
-
});
|
|
611
|
-
};
|
|
612
|
-
this.scannerModule.on("scanner:run:skip", this.onScannerRunSkip);
|
|
613
|
-
this.scannerModule.on("scanner:run:error", this.onScannerRunError);
|
|
614
561
|
this.signalConsumerDone = this.runSignalConsumer();
|
|
615
562
|
logger.info("Runtime.start() invoking scannerModule.start()");
|
|
616
563
|
await this.scannerModule.start();
|
|
@@ -654,14 +601,6 @@ export class Runtime {
|
|
|
654
601
|
this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
|
|
655
602
|
this.onScannerRunComplete = null;
|
|
656
603
|
}
|
|
657
|
-
if (this.onScannerRunSkip != null) {
|
|
658
|
-
this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
|
|
659
|
-
this.onScannerRunSkip = null;
|
|
660
|
-
}
|
|
661
|
-
if (this.onScannerRunError != null) {
|
|
662
|
-
this.scannerModule.off("scanner:run:error", this.onScannerRunError);
|
|
663
|
-
this.onScannerRunError = null;
|
|
664
|
-
}
|
|
665
604
|
if (this.actionNotificationSubscriber != null) {
|
|
666
605
|
this.actionNotificationSubscriber.dispose();
|
|
667
606
|
this.actionNotificationSubscriber = null;
|
|
@@ -678,25 +617,13 @@ export class Runtime {
|
|
|
678
617
|
catch (cleanupErr) {
|
|
679
618
|
logger.warn("[runtime] partial-state cleanup error on failed start (ignored)", { cleanupErr });
|
|
680
619
|
}
|
|
681
|
-
//
|
|
682
|
-
|
|
620
|
+
// Telemetry cleanup (must not leak on a failed boot); the error rethrows.
|
|
621
|
+
this.disposeSubscribers();
|
|
683
622
|
throw err;
|
|
684
623
|
}
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
config: this.config,
|
|
689
|
-
strategyAddress: this.strategyConfig.address,
|
|
690
|
-
runtimeVersion: VERSION,
|
|
691
|
-
hasDsl: this.dslPlugin != null,
|
|
692
|
-
scannerCount: this.config.scanners?.length ?? 0,
|
|
693
|
-
actionCount: this.config.actions?.length ?? 0,
|
|
694
|
-
},
|
|
695
|
-
timestamp: new Date().toISOString(),
|
|
696
|
-
});
|
|
697
|
-
// First-class event log. Carries the UNRESOLVED config hash (the form the recording identity
|
|
698
|
-
// hashes), so the printed hash matches `senpi.config.hash` on the rows — not the resolved config
|
|
699
|
-
// the bus event carries. The build runs inside emitEvent's guard — telemetry never blocks boot.
|
|
624
|
+
// First-class event log. Carries the UNRESOLVED config hash (the form `computeConfigHash` uses),
|
|
625
|
+
// so the printed hash matches `senpi.config.hash` on the spans/logs. The build runs inside
|
|
626
|
+
// emitEvent's guard — telemetry never blocks boot.
|
|
700
627
|
emitEvent(logger, () => buildRuntimeStartedEvent({
|
|
701
628
|
address: this.strategyConfig.address,
|
|
702
629
|
recipeName: this.config.name,
|
|
@@ -706,7 +633,50 @@ export class Runtime {
|
|
|
706
633
|
actionCount: this.config.actions?.length ?? 0,
|
|
707
634
|
hasDsl: this.dslPlugin != null,
|
|
708
635
|
unresolvedConfig: this.options.unresolvedConfig,
|
|
636
|
+
slots: this.strategyConfig.slots,
|
|
637
|
+
tradingRisk: this.strategyConfig.tradingRisk,
|
|
638
|
+
marginPerSlot: this.strategyConfig.marginPerSlot,
|
|
639
|
+
marginPct: this.strategyConfig.marginPct,
|
|
640
|
+
defaultLeverage: this.strategyConfig.defaultLeverage,
|
|
709
641
|
}));
|
|
642
|
+
// Observable gauge of the serial signal-queue depth — its absence/stall is alertable without a
|
|
643
|
+
// trace or log scan. Reads the live depth at each collection; disposed in stop().
|
|
644
|
+
const walletDim = this.strategyConfig.address.toLowerCase();
|
|
645
|
+
const group = this.config.group;
|
|
646
|
+
this.queueDepthGaugeDispose = registerQueueDepthGauge({
|
|
647
|
+
strategyAddress: walletDim,
|
|
648
|
+
group,
|
|
649
|
+
read: () => this.getSignalQueueDepth(),
|
|
650
|
+
});
|
|
651
|
+
// Liveness heartbeat: a positive tick every window, dimensioned by wallet/group. A dead runtime
|
|
652
|
+
// can't emit a "down" event — only the absence of these ticks detects it. Fire one now so a
|
|
653
|
+
// runtime that dies before its first window still leaves a mark. Unref'd so it never holds the
|
|
654
|
+
// process open; cleared in stop().
|
|
655
|
+
const windowSec = this.config.health?.window_seconds ?? DEFAULT_LIVENESS_WINDOW_SECONDS;
|
|
656
|
+
recordRuntimeHeartbeat({ strategyAddress: walletDim, group });
|
|
657
|
+
this.livenessHeartbeatTimer = setInterval(() => recordRuntimeHeartbeat({ strategyAddress: walletDim, group }), windowSec * 1000);
|
|
658
|
+
this.livenessHeartbeatTimer.unref?.();
|
|
659
|
+
// Observable gauge of active DSL-managed positions, split by exit phase. Only meaningful when this
|
|
660
|
+
// runtime runs the exit engine; counts this wallet's positions live at each collection (no snapshot).
|
|
661
|
+
if (this.dslPlugin) {
|
|
662
|
+
this.dslActivePositionsGaugeDispose = registerDslActivePositionsGauge({
|
|
663
|
+
strategyAddress: walletDim,
|
|
664
|
+
group,
|
|
665
|
+
read: () => {
|
|
666
|
+
let phase1 = 0;
|
|
667
|
+
let phase2 = 0;
|
|
668
|
+
for (const p of this.dslPlugin?.listActivePositions() ?? []) {
|
|
669
|
+
if (p.address?.toLowerCase() !== walletDim)
|
|
670
|
+
continue;
|
|
671
|
+
if (p.phase === 2)
|
|
672
|
+
phase2++;
|
|
673
|
+
else
|
|
674
|
+
phase1++;
|
|
675
|
+
}
|
|
676
|
+
return { phase1, phase2 };
|
|
677
|
+
},
|
|
678
|
+
});
|
|
679
|
+
}
|
|
710
680
|
}
|
|
711
681
|
/**
|
|
712
682
|
* Cancel exchange SLs and archive DSL state for the strategy, then log removal.
|
|
@@ -722,7 +692,7 @@ export class Runtime {
|
|
|
722
692
|
}
|
|
723
693
|
/** Tear down DSL for the runtime strategy before deleting the runtime (gateway / CLI). */
|
|
724
694
|
async teardownDslForRuntimeDeletion() {
|
|
725
|
-
|
|
695
|
+
await this.removeStrategy(this.strategyConfig.address);
|
|
726
696
|
}
|
|
727
697
|
/**
|
|
728
698
|
* Resolve the per-boot trace identity once. The envelope is immutable per boot, so build it
|
|
@@ -758,14 +728,14 @@ export class Runtime {
|
|
|
758
728
|
if (!item)
|
|
759
729
|
continue;
|
|
760
730
|
// Pin tracking context for this signal flow so LLM spans (emitted by
|
|
761
|
-
// OpenClawLlmDecision / AnthropicLlmDecision) can stamp
|
|
731
|
+
// OpenClawLlmDecision / AnthropicLlmDecision) can stamp run id,
|
|
762
732
|
// address, recipe_name, runtime_id, and runtime_version automatically.
|
|
763
733
|
const queuedItem = item;
|
|
764
734
|
// Resolved once here so the same identity rides both the trace (via the span stamp)
|
|
765
735
|
// and the logs (via the context bag below) — one source, no drift.
|
|
766
736
|
const identity = this.resolveTraceIdentity();
|
|
767
737
|
await trackingContext.run({
|
|
768
|
-
|
|
738
|
+
runId: queuedItem.runId,
|
|
769
739
|
address: queuedItem.result.address,
|
|
770
740
|
recipeName: this.config.name,
|
|
771
741
|
runtimeId: this.options.runtimeId ?? "",
|
|
@@ -806,7 +776,7 @@ export class Runtime {
|
|
|
806
776
|
// Identity (incl. strategy address) rides every span via the per-trace stamp below;
|
|
807
777
|
// run.id is the join key back to the producing scanner run.
|
|
808
778
|
const spanAttrs = {};
|
|
809
|
-
if (queuedItem.runId
|
|
779
|
+
if (queuedItem.runId) {
|
|
810
780
|
spanAttrs["senpi.run.id"] = queuedItem.runId;
|
|
811
781
|
}
|
|
812
782
|
// Carry the producing scanner id so the signal span joins/filters by scanner,
|
|
@@ -822,8 +792,10 @@ export class Runtime {
|
|
|
822
792
|
// Measured from enqueue, but the pre-gate backlog is evicted above before the
|
|
823
793
|
// span opens, so every signal that reaches here was enqueued after the (one-shot)
|
|
824
794
|
// reconcile gate resolved — i.e. this is pure FIFO wait, not gate wait.
|
|
825
|
-
|
|
795
|
+
const queueWaitMs = Date.now() - queuedItem.enqueuedAt;
|
|
796
|
+
span.setAttribute("senpi.queue.wait_ms", queueWaitMs);
|
|
826
797
|
span.setAttribute("senpi.queue.depth", this.signalQueue.length);
|
|
798
|
+
recordSignalQueueWait(queueWaitMs / 1000, { scannerId: queuedItem.result.scannerId });
|
|
827
799
|
try {
|
|
828
800
|
const usedSlotsByStrategy = await this.stateManager.getUsedSlotsByStrategy([this.strategyConfig.address]);
|
|
829
801
|
const payload = buildActionScanPayload(queuedItem.result, this.strategyConfig, {
|
|
@@ -836,7 +808,6 @@ export class Runtime {
|
|
|
836
808
|
address: queuedItem.result.address,
|
|
837
809
|
data: {
|
|
838
810
|
...payload,
|
|
839
|
-
__correlationId: queuedItem.correlationId,
|
|
840
811
|
__scanRunId: queuedItem.runId,
|
|
841
812
|
},
|
|
842
813
|
timestamp: new Date().toISOString(),
|
|
@@ -924,23 +895,6 @@ export class Runtime {
|
|
|
924
895
|
}
|
|
925
896
|
const durationMs = Date.now() - t0;
|
|
926
897
|
const { adopted, resized, archived } = result.counts;
|
|
927
|
-
// Telemetry must never break reconcile control flow. bus.emit is a Node
|
|
928
|
-
// EventEmitter: a listener that throws synchronously makes emit re-throw,
|
|
929
|
-
// which would reject this loop and leave the gate forever closed. Contain it.
|
|
930
|
-
try {
|
|
931
|
-
this.bus.emit("tracking.runtime_reconciled", {
|
|
932
|
-
address,
|
|
933
|
-
data: { adopted, resized, archived, success: result.success, attempt, durationMs },
|
|
934
|
-
timestamp: new Date().toISOString(),
|
|
935
|
-
});
|
|
936
|
-
}
|
|
937
|
-
catch (err) {
|
|
938
|
-
logger.warn("[runtime][reconcile] tracking.runtime_reconciled listener threw; contained", {
|
|
939
|
-
address,
|
|
940
|
-
attempt,
|
|
941
|
-
err,
|
|
942
|
-
});
|
|
943
|
-
}
|
|
944
898
|
if (result.success) {
|
|
945
899
|
logger.info("[runtime][reconcile] boot reconcile succeeded", { address, attempt, adopted, resized, archived });
|
|
946
900
|
this.resolveReconcileGate();
|
|
@@ -959,22 +913,14 @@ export class Runtime {
|
|
|
959
913
|
gate.resolve();
|
|
960
914
|
}
|
|
961
915
|
/**
|
|
962
|
-
* Dispose the
|
|
963
|
-
*
|
|
916
|
+
* Dispose the domain-event subscriber. Idempotent; shared by stop() and the failed-start
|
|
917
|
+
* cleanup path in start().
|
|
964
918
|
*/
|
|
965
|
-
|
|
966
|
-
if (this.recordingSubscriber) {
|
|
967
|
-
this.recordingSubscriber.dispose();
|
|
968
|
-
this.recordingSubscriber = null;
|
|
969
|
-
}
|
|
919
|
+
disposeSubscribers() {
|
|
970
920
|
if (this.domainEventSubscriber) {
|
|
971
921
|
this.domainEventSubscriber.dispose();
|
|
972
922
|
this.domainEventSubscriber = null;
|
|
973
923
|
}
|
|
974
|
-
if (this.recordingBatcher) {
|
|
975
|
-
await this.recordingBatcher.shutdown().catch(() => { });
|
|
976
|
-
this.recordingBatcher = null;
|
|
977
|
-
}
|
|
978
924
|
}
|
|
979
925
|
async stop() {
|
|
980
926
|
if (!this.started)
|
|
@@ -989,18 +935,20 @@ export class Runtime {
|
|
|
989
935
|
// Resolve the gate so the consumer doesn't hang waiting for a reconcile that won't come.
|
|
990
936
|
this.reconcileGate?.resolve();
|
|
991
937
|
const uptimeMs = Date.now() - (this.startedAt ?? Date.now());
|
|
992
|
-
|
|
993
|
-
address: this.strategyConfig.address,
|
|
994
|
-
data: {
|
|
995
|
-
uptimeMs,
|
|
996
|
-
runtimeVersion: VERSION,
|
|
997
|
-
strategyAddress: this.strategyConfig.address,
|
|
998
|
-
},
|
|
999
|
-
timestamp: new Date().toISOString(),
|
|
1000
|
-
});
|
|
1001
|
-
// First-class event log alongside the legacy bus emit. The build runs inside emitEvent's guard
|
|
1002
|
-
// — telemetry never blocks shutdown.
|
|
938
|
+
// First-class event log. The build runs inside emitEvent's guard — telemetry never blocks shutdown.
|
|
1003
939
|
emitEvent(logger, () => buildRuntimeStoppedEvent(VERSION, uptimeMs, this.strategyConfig.address));
|
|
940
|
+
if (this.queueDepthGaugeDispose) {
|
|
941
|
+
this.queueDepthGaugeDispose();
|
|
942
|
+
this.queueDepthGaugeDispose = null;
|
|
943
|
+
}
|
|
944
|
+
if (this.dslActivePositionsGaugeDispose) {
|
|
945
|
+
this.dslActivePositionsGaugeDispose();
|
|
946
|
+
this.dslActivePositionsGaugeDispose = null;
|
|
947
|
+
}
|
|
948
|
+
if (this.livenessHeartbeatTimer) {
|
|
949
|
+
clearInterval(this.livenessHeartbeatTimer);
|
|
950
|
+
this.livenessHeartbeatTimer = null;
|
|
951
|
+
}
|
|
1004
952
|
this.signalConsumerStopped = true;
|
|
1005
953
|
if (this.signalQueueWait) {
|
|
1006
954
|
const w = this.signalQueueWait;
|
|
@@ -1016,14 +964,6 @@ export class Runtime {
|
|
|
1016
964
|
this.scannerModule.off("scanner:run:complete", this.onScannerRunComplete);
|
|
1017
965
|
this.onScannerRunComplete = null;
|
|
1018
966
|
}
|
|
1019
|
-
if (this.onScannerRunSkip != null) {
|
|
1020
|
-
this.scannerModule.off("scanner:run:skip", this.onScannerRunSkip);
|
|
1021
|
-
this.onScannerRunSkip = null;
|
|
1022
|
-
}
|
|
1023
|
-
if (this.onScannerRunError != null) {
|
|
1024
|
-
this.scannerModule.off("scanner:run:error", this.onScannerRunError);
|
|
1025
|
-
this.onScannerRunError = null;
|
|
1026
|
-
}
|
|
1027
967
|
if (this.actionNotificationSubscriber != null) {
|
|
1028
968
|
this.actionNotificationSubscriber.dispose();
|
|
1029
969
|
this.actionNotificationSubscriber = null;
|
|
@@ -1038,13 +978,7 @@ export class Runtime {
|
|
|
1038
978
|
this.senpi = null;
|
|
1039
979
|
}
|
|
1040
980
|
this.healthStateModule = null;
|
|
1041
|
-
|
|
1042
|
-
await this.trackingHandler.dispose();
|
|
1043
|
-
this.trackingHandler = null;
|
|
1044
|
-
}
|
|
1045
|
-
// Telemetry recording teardown: stop the subscriber, then flush + stop the batcher (best-effort)
|
|
1046
|
-
// so the just-recorded runtime_stopped row ships before the process exits.
|
|
1047
|
-
await this.disposeRecording();
|
|
981
|
+
this.disposeSubscribers();
|
|
1048
982
|
if (this.bus.removeAllListeners) {
|
|
1049
983
|
this.bus.removeAllListeners();
|
|
1050
984
|
}
|