@junando/core 0.12.3 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -556,6 +556,13 @@ interface IAlertQueue {
556
556
  interface ITraceRepository {
557
557
  findByTraceId(traceId: string): Promise<Record<string, unknown>[]>;
558
558
  }
559
+ /**
560
+ * Reason a parse attempt could not produce a diagnosis.
561
+ * `unparseable_response` — non-empty raw text that neither JSON nor regex
562
+ * stage could extract a usable analysis from.
563
+ * `empty_response` — the provider returned an empty (or whitespace-only) string.
564
+ */
565
+ type LlmDegradedReason = 'unparseable_response' | 'empty_response';
559
566
  /**
560
567
  * Structured result of an LLM analysis call.
561
568
  * Carries the diagnosis plus the observability metadata that feeds the
@@ -563,7 +570,8 @@ interface ITraceRepository {
563
570
  * total tokens = promptTokens + completionTokens).
564
571
  */
565
572
  interface LLMResult {
566
- analysis: LLMAnalysis;
573
+ analysis: LLMAnalysis | null;
574
+ degradedReason?: LlmDegradedReason;
567
575
  provider: string;
568
576
  model: string;
569
577
  latencyMs: number;
@@ -776,7 +784,10 @@ interface LlmSection {
776
784
  provider: string;
777
785
  model: string;
778
786
  latencyMs: number;
779
- urgency: string;
787
+ /** Present only when parseAnalysis produced a diagnosis. */
788
+ urgency?: string;
789
+ /** Present only when analysis is null — why no diagnosis was produced. */
790
+ degradedReason?: string;
780
791
  tokens: number;
781
792
  }
782
793
  interface NotifySection {
@@ -868,6 +879,13 @@ export declare class ProcessIncidentUseCase {
868
879
  private readonly wideEventsEnabled;
869
880
  constructor(deps: Dependencies);
870
881
  execute(alerts: NormalizedAlert[], correlationId: string): Promise<void>;
882
+ /**
883
+ * Releases the dedup claim so an SQS retry is not discarded as a duplicate.
884
+ * Concurrency: safe without a lock — sqs.adapter.ts sets messageGroupId to
885
+ * the fingerprint, so SQS FIFO serialises delivery per fingerprint and two
886
+ * runs for the same fingerprint are never in flight simultaneously.
887
+ */
888
+ private releaseDedupClaim;
871
889
  /**
872
890
  * Flushes the builder into a final event, applies tail sampling, redacts
873
891
  * PII, and emits the single canonical log line for the cluster.
@@ -890,6 +908,17 @@ export declare class InMemoryDeduplicationStore implements IDeduplicationStore {
890
908
  clear(): void;
891
909
  }
892
910
  //#endregion
911
+ //#region src/infrastructure/dedup/dynamodb-dedup.adapter.d.ts
912
+ export declare class DynamoDBDeduplicationStore implements IDeduplicationStore {
913
+ private readonly tableName;
914
+ private readonly region?;
915
+ private client;
916
+ constructor(tableName: string, region?: string | undefined);
917
+ private getClient;
918
+ isNew(fingerprint: string, ttlSeconds: number): Promise<DedupResult>;
919
+ reset(fingerprint: string): Promise<void>;
920
+ }
921
+ //#endregion
893
922
  //#region src/infrastructure/indexer/opensearch.adapter.d.ts
894
923
  interface SignedHttpRequest {
895
924
  method: string;
@@ -992,7 +1021,12 @@ declare const ConfigSchema: z.ZodObject<{
992
1021
  rollbackActionAllowedSlackUserIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
993
1022
  teamsWebhookUrl: z.ZodOptional<z.ZodString>;
994
1023
  lokiUrl: z.ZodPipe<z.ZodOptional<z.ZodString>, z.ZodTransform<string | undefined, string | undefined>>;
995
- redisUrl: z.ZodString;
1024
+ dedupStore: z.ZodDefault<z.ZodEnum<{
1025
+ dynamodb: "dynamodb";
1026
+ redis: "redis";
1027
+ }>>;
1028
+ dedupTableName: z.ZodOptional<z.ZodString>;
1029
+ redisUrl: z.ZodOptional<z.ZodString>;
996
1030
  sqsQueueUrl: z.ZodUnion<[z.ZodOptional<z.ZodString>, z.ZodLiteral<"">]>;
997
1031
  dedupTtlSeconds: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
998
1032
  clusterWindowMs: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
@@ -1258,5 +1292,5 @@ export declare class FactoryRegistry<T> {
1258
1292
  */
1259
1293
  export declare function flushLoki(): Promise<void>;
1260
1294
  //#endregion
1261
- export { type Config, type DedupResult, type IAlertQueue, type IDeduplicationStore, type IIndexer, type ILLMProvider, type INotifier, type IRollbackActionHandler, type IRuleEngine, type ITraceRepository, type LLMResult, type Logger, type LoggerOptions, type NotifyResult, type OpenSearchHttpFetcher, type OpenSearchHttpResponse, type OpenSearchIndexerDeps, type RollbackActionRequest, type RollbackActionResult, type RuleActionResult, type SignedHttpRequest, type TraceabilityDocument, index_d_exports as metrics };
1295
+ export { type Config, type DedupResult, type IAlertQueue, type IDeduplicationStore, type IIndexer, type ILLMProvider, type INotifier, type IRollbackActionHandler, type IRuleEngine, type ITraceRepository, type LLMResult, type LlmDegradedReason, type Logger, type LoggerOptions, type NotifyResult, type OpenSearchHttpFetcher, type OpenSearchHttpResponse, type OpenSearchIndexerDeps, type RollbackActionRequest, type RollbackActionResult, type RuleActionResult, type SignedHttpRequest, type TraceabilityDocument, index_d_exports as metrics };
1262
1296
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/shared/constants.ts","../src/domain/entities/alert.ts","../src/domain/entities/cluster.ts","../src/domain/entities/incident.ts","../src/domain/entities/rule.ts","../src/domain/entities/traceability.ts","../src/domain/value-objects/fingerprint.ts","../src/domain/ports/index.ts","../src/domain/services/clustering.service.ts","../src/application/dtos/normalize-payload.ts","../src/shared/logger/enums.ts","../src/shared/logger/wide-event-builder.ts","../src/shared/logger/index.ts","../src/application/use-cases/process-incident.use-case.ts","../src/infrastructure/dedup/redis-dedup.adapter.ts","../src/infrastructure/indexer/opensearch.adapter.ts","../src/infrastructure/llm/llm.adapter.ts","../src/shared/config/index.ts","../src/infrastructure/rules/channel-registry.ts","../src/infrastructure/notifier/factory.ts","../src/infrastructure/rollback/factory.ts","../src/infrastructure/rollback/noop-rollback-action.handler.ts","../src/infrastructure/notifier/slack.adapter.ts","../src/infrastructure/notifier/teams.adapter.ts","../src/infrastructure/notifier/routing-notifier.ts","../src/infrastructure/queue/sqs.adapter.ts","../src/infrastructure/queue/sqs-lag-poller.ts","../src/infrastructure/traces/loki-trace.adapter.ts","../src/infrastructure/rules/yaml-rule-loader.ts","../src/infrastructure/rules/condition-evaluator.ts","../src/infrastructure/rules/action-dispatcher.ts","../src/infrastructure/rules/rule-engine.ts","../src/shared/factory-registry.ts","../src/shared/logger/loki-transport.ts"],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/shared/constants.ts","../src/domain/entities/alert.ts","../src/domain/entities/cluster.ts","../src/domain/entities/incident.ts","../src/domain/entities/rule.ts","../src/domain/entities/traceability.ts","../src/domain/value-objects/fingerprint.ts","../src/domain/ports/index.ts","../src/domain/services/clustering.service.ts","../src/application/dtos/normalize-payload.ts","../src/shared/logger/enums.ts","../src/shared/logger/wide-event-builder.ts","../src/shared/logger/index.ts","../src/application/use-cases/process-incident.use-case.ts","../src/infrastructure/dedup/redis-dedup.adapter.ts","../src/infrastructure/dedup/dynamodb-dedup.adapter.ts","../src/infrastructure/indexer/opensearch.adapter.ts","../src/infrastructure/llm/llm.adapter.ts","../src/shared/config/index.ts","../src/infrastructure/rules/channel-registry.ts","../src/infrastructure/notifier/factory.ts","../src/infrastructure/rollback/factory.ts","../src/infrastructure/rollback/noop-rollback-action.handler.ts","../src/infrastructure/notifier/slack.adapter.ts","../src/infrastructure/notifier/teams.adapter.ts","../src/infrastructure/notifier/routing-notifier.ts","../src/infrastructure/queue/sqs.adapter.ts","../src/infrastructure/queue/sqs-lag-poller.ts","../src/infrastructure/traces/loki-trace.adapter.ts","../src/infrastructure/rules/yaml-rule-loader.ts","../src/infrastructure/rules/condition-evaluator.ts","../src/infrastructure/rules/action-dispatcher.ts","../src/infrastructure/rules/rule-engine.ts","../src/shared/factory-registry.ts","../src/shared/logger/loki-transport.ts"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM, r as __exportAll, t as __commonJSMin } from "./rolldown-runtime-BMI-E3GI.js";
2
- import { dedupDuplicate, dedupNew, dedupRedisFailoverTotal, llmInferenceDuration, llmInferenceTotal, notificationsTotal, sqsQueueLag, suppressedClusters, t as metrics_exports } from "./shared/metrics/index.js";
2
+ import { dedupDuplicate, dedupFailoverTotal, dedupNew, llmInferenceDuration, llmInferenceTotal, notificationsTotal, sqsQueueLag, suppressedClusters, t as metrics_exports } from "./shared/metrics/index.js";
3
3
  import { $ as booleanSelector, $t as ServiceException, A as BinaryDecisionDiagram, An as isValidHostLabel, B as NODE_REGION_CONFIG_FILE_OPTIONS, C as isIpAddress, Ct as dateToUtcString, Dt as parseRfc7231DateTime, E as EndpointError, En as normalizeProvider$1, Et as parseRfc3339DateTimeWithOffset, F as init_config$1, Fn as hasOwn, G as NODE_USE_FIPS_ENDPOINT_CONFIG_OPTIONS, H as REGION_ENV_NAME, Ht as NoOpLogger, It as fromBase64, Jt as resolveDefaultRuntimeConfig, Kt as getDefaultExtensionConfiguration, L as resolveDefaultsModeConfig, Ln as getSmithyContext, N as resolveParams, Nn as HttpRequest, Nt as toBase64, O as EndpointCache, On as isValidHostname, Ot as expectUnion, P as config_exports, Pt as fromUtf8, Qt as loadConfigsForDefaultMode, Rt as client_exports$1, Sn as parseUrl, Tt as parseEpochTimestamp, U as REGION_INI_NAME, V as NODE_REGION_CONFIG_OPTIONS, Vt as makeBuilder, Wt as getValueFromTextNode, X as loadConfig$1, Yt as emitWarningIfUnsupportedVersion$1, Z as SelectorType, _ as resolveEndpointConfig, _t as _parseRfc7231DateTime, b as decideEndpoint, bn as init_transport, bt as quoteHeader, c as generateIdempotencyToken, cn as TypeRegistry, ct as NumericValue, d as v4, en as decorateServiceException, ft as splitHeader, g as init_endpoints, gn as deref, gt as _parseRfc3339DateTimeWithOffset, h as getEndpointPlugin, ht as _parseEpochTimestamp, in as Command, jn as HttpResponse, jt as toUtf8, l as init_serde, m as endpoints_exports, mn as getSchemaSerdePlugin, mt as splitEvery, n as init_checksum, nn as createAggregatedClient, on as init_schema, p as sdkStreamMixin, pn as translateTraits, q as NODE_USE_DUALSTACK_ENDPOINT_CONFIG_OPTIONS, r as Sha256Node, rt as calculateBodyLength, s as Uint8ArrayBlobAdapter, sn as schema_exports, t as checksum_exports, tt as ProviderError, u as serde_exports, un as NormalizedSchema, ut as nv, vn as Client, w as customEndpointFunctions, wn as parseQueryString, xt as LazyJsonString, y as resolveEndpoint, z as resolveRegionConfig, zn as require_dist_cjs$17, zt as init_client$1 } from "./checksum-Bbvsykod.js";
4
4
  import { z } from "zod";
5
5
  import { createHash, randomUUID } from "node:crypto";
6
6
  import pino from "pino";
7
7
  import { Readable, Writable } from "node:stream";
8
+ import { DeleteItemCommand, DynamoDBClient, PutItemCommand } from "@aws-sdk/client-dynamodb";
8
9
  import * as Breaker from "opossum";
9
10
  import { readFileSync } from "node:fs";
10
11
  import { YAMLParseError, parse } from "yaml";
@@ -487,6 +488,58 @@ var WideEventBuilder = class {
487
488
  };
488
489
  }
489
490
  };
491
+ //#endregion
492
+ //#region src/shared/logger/enums.ts
493
+ /**
494
+ * Pipeline component taxonomy.
495
+ *
496
+ * `component` (not `service`) distinguishes pipeline stages in wide events.
497
+ * `service` stays constant ("junando"); `component` tells you WHERE the event
498
+ * was emitted from.
499
+ */
500
+ const Component = {
501
+ Webhook: "webhook",
502
+ Worker: "worker",
503
+ UseCase: "useCase",
504
+ Llm: "llm",
505
+ Notifier: "notifier",
506
+ Dedup: "dedup",
507
+ Traces: "traces",
508
+ Ingest: "ingest",
509
+ Rollback: "rollback"
510
+ };
511
+ /**
512
+ * Pipeline stages that write their results into the WideEventBuilder.
513
+ */
514
+ const Stage = {
515
+ Dedup: "dedup",
516
+ RulesPre: "rulesPre",
517
+ Traces: "traces",
518
+ Llm: "llm",
519
+ RulesPost: "rulesPost",
520
+ Notify: "notify"
521
+ };
522
+ /**
523
+ * Terminal outcomes for a wide event across all entry points.
524
+ */
525
+ const Outcome = {
526
+ Success: "success",
527
+ Suppressed: "suppressed",
528
+ Degraded: "degraded",
529
+ Error: "error",
530
+ Accepted: "accepted",
531
+ Empty: "empty",
532
+ ParseError: "parse_error"
533
+ };
534
+ /**
535
+ * Reason recorded for a tail-sampling decision.
536
+ */
537
+ const SamplingDecision = {
538
+ Error: "error",
539
+ Slow: "slow",
540
+ Random: "random",
541
+ Skipped: "skipped"
542
+ };
490
543
  /** Probability of sampling a normal (non-error, non-slow) event. */
491
544
  const NORMAL_SAMPLE_RATE = .05;
492
545
  /**
@@ -498,6 +551,7 @@ const NORMAL_SAMPLE_RATE = .05;
498
551
  function shouldSample(event) {
499
552
  if (event.error != null) return true;
500
553
  if (event.durationMs !== void 0 && event.durationMs > 1e4) return true;
554
+ if (event.outcome === Outcome.Degraded) return true;
501
555
  return Math.random() < NORMAL_SAMPLE_RATE;
502
556
  }
503
557
  //#endregion
@@ -568,58 +622,6 @@ function redact(obj) {
568
622
  }));
569
623
  }
570
624
  //#endregion
571
- //#region src/shared/logger/enums.ts
572
- /**
573
- * Pipeline component taxonomy.
574
- *
575
- * `component` (not `service`) distinguishes pipeline stages in wide events.
576
- * `service` stays constant ("junando"); `component` tells you WHERE the event
577
- * was emitted from.
578
- */
579
- const Component = {
580
- Webhook: "webhook",
581
- Worker: "worker",
582
- UseCase: "useCase",
583
- Llm: "llm",
584
- Notifier: "notifier",
585
- Dedup: "dedup",
586
- Traces: "traces",
587
- Ingest: "ingest",
588
- Rollback: "rollback"
589
- };
590
- /**
591
- * Pipeline stages that write their results into the WideEventBuilder.
592
- */
593
- const Stage = {
594
- Dedup: "dedup",
595
- RulesPre: "rulesPre",
596
- Traces: "traces",
597
- Llm: "llm",
598
- RulesPost: "rulesPost",
599
- Notify: "notify"
600
- };
601
- /**
602
- * Terminal outcomes for a wide event across all entry points.
603
- */
604
- const Outcome = {
605
- Success: "success",
606
- Suppressed: "suppressed",
607
- Degraded: "degraded",
608
- Error: "error",
609
- Accepted: "accepted",
610
- Empty: "empty",
611
- ParseError: "parse_error"
612
- };
613
- /**
614
- * Reason recorded for a tail-sampling decision.
615
- */
616
- const SamplingDecision = {
617
- Error: "error",
618
- Slow: "slow",
619
- Random: "random",
620
- Skipped: "skipped"
621
- };
622
- //#endregion
623
625
  //#region src/shared/logger/index.ts
624
626
  let _root = buildLogger({});
625
627
  function buildLogger(opts) {
@@ -689,12 +691,14 @@ function toErrorSection(err) {
689
691
  }
690
692
  /**
691
693
  * Terminal outcome for a processed cluster. Early returns — no switch/case.
692
- * Notify failure is fatal (the batch is retried via SQS); LLM failure is
693
- * degraded (notification still went out without a diagnosis).
694
+ * Notify failure is fatal (the batch is retried via SQS); LLM transport
695
+ * failure and a parse-degraded diagnosis both leave the cluster degraded
696
+ * (notification still went out, with or without a diagnosis).
694
697
  */
695
- function resolveOutcome({ llmError, notifyError }) {
698
+ function resolveOutcome({ llmError, notifyError, llmDegradedReason }) {
696
699
  if (notifyError != null) return Outcome.Error;
697
700
  if (llmError != null) return Outcome.Degraded;
701
+ if (llmDegradedReason != null) return Outcome.Degraded;
698
702
  return Outcome.Success;
699
703
  }
700
704
  var ProcessIncidentUseCase = class {
@@ -762,14 +766,17 @@ var ProcessIncidentUseCase = class {
762
766
  });
763
767
  let analysis = null;
764
768
  let llmError = null;
769
+ let llmDegradedReason = null;
765
770
  try {
766
771
  const llmResult = await llm.analyze(cluster, allSpans);
767
772
  analysis = llmResult.analysis;
773
+ llmDegradedReason = llmResult.degradedReason ?? null;
768
774
  builder.set("llm", {
769
775
  provider: llmResult.provider,
770
776
  model: llmResult.model,
771
777
  latencyMs: llmResult.latencyMs,
772
- urgency: llmResult.analysis.urgency_level,
778
+ ...llmResult.analysis && { urgency: llmResult.analysis.urgency_level },
779
+ ...llmResult.degradedReason && { degradedReason: llmResult.degradedReason },
773
780
  tokens: llmResult.promptTokens + llmResult.completionTokens
774
781
  });
775
782
  } catch (err) {
@@ -802,17 +809,35 @@ var ProcessIncidentUseCase = class {
802
809
  latencyMs: Date.now() - notifyStartMs
803
810
  });
804
811
  builder.set("error", toErrorSection(err));
812
+ await this.releaseDedupClaim(cluster.fingerprint);
805
813
  this.emit(builder, Outcome.Error, clusterStartMs);
806
814
  throw err;
807
815
  }
808
816
  if (llmError != null) builder.set("error", toErrorSection(llmError));
809
817
  this.emit(builder, resolveOutcome({
810
818
  llmError,
811
- notifyError: null
819
+ notifyError: null,
820
+ llmDegradedReason
812
821
  }), clusterStartMs);
813
822
  }
814
823
  }
815
824
  /**
825
+ * Releases the dedup claim so an SQS retry is not discarded as a duplicate.
826
+ * Concurrency: safe without a lock — sqs.adapter.ts sets messageGroupId to
827
+ * the fingerprint, so SQS FIFO serialises delivery per fingerprint and two
828
+ * runs for the same fingerprint are never in flight simultaneously.
829
+ */
830
+ async releaseDedupClaim(fingerprint) {
831
+ try {
832
+ await this.deps.dedup.reset(fingerprint);
833
+ } catch (err) {
834
+ this.deps.logger.warn({
835
+ err,
836
+ fingerprint
837
+ }, "Failed to release dedup claim; retry may be suppressed");
838
+ }
839
+ }
840
+ /**
816
841
  * Flushes the builder into a final event, applies tail sampling, redacts
817
842
  * PII, and emits the single canonical log line for the cluster.
818
843
  */
@@ -825,7 +850,7 @@ var ProcessIncidentUseCase = class {
825
850
  };
826
851
  //#endregion
827
852
  //#region src/infrastructure/dedup/redis-dedup.adapter.ts
828
- const logger$6 = createLogger();
853
+ const logger$7 = createLogger();
829
854
  var RedisDeduplicationStore = class {
830
855
  redis;
831
856
  keyPrefix = "junando:dedup:";
@@ -840,11 +865,11 @@ var RedisDeduplicationStore = class {
840
865
  };
841
866
  } catch (err) {
842
867
  const message = err instanceof Error ? err.message : String(err);
843
- logger$6.warn({
868
+ logger$7.warn({
844
869
  err,
845
870
  fingerprint
846
871
  }, "Redis dedup check failed, failing open");
847
- dedupRedisFailoverTotal.inc();
872
+ dedupFailoverTotal.inc();
848
873
  return {
849
874
  isNew: true,
850
875
  ttlSeconds,
@@ -879,6 +904,63 @@ var InMemoryDeduplicationStore = class {
879
904
  }
880
905
  };
881
906
  //#endregion
907
+ //#region src/infrastructure/dedup/dynamodb-dedup.adapter.ts
908
+ const logger$6 = createLogger();
909
+ const CONDITION_EXPRESSION = "attribute_not_exists(fingerprint) OR expiresAt < :now";
910
+ var DynamoDBDeduplicationStore = class {
911
+ tableName;
912
+ region;
913
+ client = null;
914
+ constructor(tableName, region) {
915
+ this.tableName = tableName;
916
+ this.region = region;
917
+ }
918
+ getClient() {
919
+ if (!this.client) this.client = new DynamoDBClient(this.region ? { region: this.region } : {});
920
+ return this.client;
921
+ }
922
+ async isNew(fingerprint, ttlSeconds) {
923
+ const nowSec = Math.floor(Date.now() / 1e3);
924
+ try {
925
+ await this.getClient().send(new PutItemCommand({
926
+ TableName: this.tableName,
927
+ Item: {
928
+ fingerprint: { S: fingerprint },
929
+ expiresAt: { N: String(nowSec + ttlSeconds) }
930
+ },
931
+ ConditionExpression: CONDITION_EXPRESSION,
932
+ ExpressionAttributeValues: { ":now": { N: String(nowSec) } }
933
+ }));
934
+ return {
935
+ isNew: true,
936
+ ttlSeconds
937
+ };
938
+ } catch (err) {
939
+ if (err instanceof Error && err.name === "ConditionalCheckFailedException") return {
940
+ isNew: false,
941
+ ttlSeconds
942
+ };
943
+ const message = err instanceof Error ? err.message : String(err);
944
+ logger$6.warn({
945
+ err,
946
+ fingerprint
947
+ }, "DynamoDB dedup check failed, failing open");
948
+ dedupFailoverTotal.inc();
949
+ return {
950
+ isNew: true,
951
+ ttlSeconds,
952
+ error: message
953
+ };
954
+ }
955
+ }
956
+ async reset(fingerprint) {
957
+ await this.getClient().send(new DeleteItemCommand({
958
+ TableName: this.tableName,
959
+ Key: { fingerprint: { S: fingerprint } }
960
+ }));
961
+ }
962
+ };
963
+ //#endregion
882
964
  //#region src/infrastructure/indexer/opensearch.adapter.ts
883
965
  var OpenSearchIndexer = class {
884
966
  endpoint;
@@ -961,20 +1043,22 @@ function buildUserPrompt(cluster, traces) {
961
1043
  }
962
1044
  /**
963
1045
  * Extracts LLMAnalysis from raw LLM response text.
964
- * Uses multi-stage parsing: JSON → regex fallback heuristics.
965
- * Returns validated LLMAnalysis or falls back to default values.
1046
+ * Two-stage parsing: JSON → regex fallback. Never fabricates a diagnosis —
1047
+ * returns null when neither stage produces a usable analysis.
966
1048
  */
967
1049
  function parseAnalysis(raw, correlationId) {
968
1050
  const startIdx = raw.indexOf("{");
969
1051
  const endIdx = raw.lastIndexOf("}");
1052
+ let stage1Succeeded = false;
970
1053
  if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) try {
971
- return LLMAnalysisSchema.parse(JSON.parse(raw.slice(startIdx, endIdx + 1)));
972
- } catch {
973
- logger$5.warn({
974
- rawResponse: raw.slice(0, 500),
975
- correlationId
976
- }, "llm:parse:failed");
977
- }
1054
+ const analysis = LLMAnalysisSchema.parse(JSON.parse(raw.slice(startIdx, endIdx + 1)));
1055
+ stage1Succeeded = true;
1056
+ return analysis;
1057
+ } catch {}
1058
+ if (!stage1Succeeded) logger$5.warn({
1059
+ rawResponse: raw.slice(0, 500),
1060
+ correlationId
1061
+ }, "llm:parse:failed");
978
1062
  const probableCauseMatch = RE_PROBABLE_CAUSE.exec(raw);
979
1063
  const urgencyMatch = RE_URGENCY_LEVEL.exec(raw);
980
1064
  const rollbackMatch = RE_REQUIRES_ROLLBACK.exec(raw);
@@ -991,20 +1075,34 @@ function parseAnalysis(raw, correlationId) {
991
1075
  urgency_level: urgency,
992
1076
  requires_rollback: rollbackMatch?.[1] === "true"
993
1077
  };
994
- return LLMAnalysisSchema.parse(analysis);
1078
+ const parsed = LLMAnalysisSchema.parse(analysis);
1079
+ logger$5.warn({
1080
+ matchedFields: ["probable_cause", "urgency_level"],
1081
+ correlationId
1082
+ }, "llm:parse:partial");
1083
+ return parsed;
1084
+ }
1085
+ logger$5.warn({ correlationId }, "llm:parse:unusable");
1086
+ return null;
1087
+ }
1088
+ /**
1089
+ * Shared entry point for turning provider text into a diagnosis (or a
1090
+ * degradedReason explaining why not). Detects empty responses once, here,
1091
+ * before delegating to parseAnalysis.
1092
+ */
1093
+ function parseLlmText(raw, correlationId) {
1094
+ if (raw.trim() === "") {
1095
+ logger$5.warn({ correlationId }, "llm:parse:empty");
1096
+ return {
1097
+ analysis: null,
1098
+ degradedReason: "empty_response"
1099
+ };
995
1100
  }
996
- const lowerRaw = raw.toLowerCase();
997
- let urgencyLevel = "medium";
998
- if (lowerRaw.includes("critical") || lowerRaw.includes("severity 1")) urgencyLevel = "critical";
999
- else if (lowerRaw.includes("high") || lowerRaw.includes("severity 2")) urgencyLevel = "high";
1000
- else if (lowerRaw.includes("low")) urgencyLevel = "low";
1001
- return LLMAnalysisSchema.parse({
1002
- probable_cause: "Analysis in progress - check logs for details",
1003
- impacted_services: ["unknown-service"],
1004
- recommended_steps: ["Review incident details in logs"],
1005
- urgency_level: urgencyLevel,
1006
- requires_rollback: lowerRaw.includes("rollback") || lowerRaw.includes("revert")
1007
- });
1101
+ const analysis = parseAnalysis(raw, correlationId);
1102
+ return analysis ? { analysis } : {
1103
+ analysis: null,
1104
+ degradedReason: "unparseable_response"
1105
+ };
1008
1106
  }
1009
1107
  /**
1010
1108
  * Gemini LLM provider using Google Generative AI SDK.
@@ -1043,7 +1141,7 @@ var GeminiProvider = class {
1043
1141
  }).generateContent(buildUserPrompt(cluster, traces));
1044
1142
  const usage = result.response.usageMetadata;
1045
1143
  return {
1046
- analysis: parseAnalysis(result.response.text()),
1144
+ ...parseLlmText(result.response.text()),
1047
1145
  promptTokens: usage?.promptTokenCount ?? 0,
1048
1146
  completionTokens: usage?.candidatesTokenCount ?? 0
1049
1147
  };
@@ -1073,7 +1171,7 @@ var ClaudeProvider = class {
1073
1171
  }]
1074
1172
  });
1075
1173
  return {
1076
- analysis: parseAnalysis(message.content.find((b) => b.type === "text")?.text ?? ""),
1174
+ ...parseLlmText(message.content.find((b) => b.type === "text")?.text ?? ""),
1077
1175
  provider: "claude",
1078
1176
  model: this.model,
1079
1177
  latencyMs: Date.now() - startMs,
@@ -1190,7 +1288,7 @@ var OpenRouterProvider = class {
1190
1288
  errors: parsed.error.format(),
1191
1289
  correlationId
1192
1290
  }, "llm:validation:failed");
1193
- const analysis = parseAnalysis(parsed.success ? parsed.data.choices?.[0]?.message?.content ?? "" : "", correlationId);
1291
+ const parsedAnalysis = parseLlmText(parsed.success ? parsed.data.choices?.[0]?.message?.content ?? "" : "", correlationId);
1194
1292
  const usage = parsed.success ? parsed.data.usage : void 0;
1195
1293
  if (usage) {
1196
1294
  const { prompt_tokens, completion_tokens, total_tokens } = usage;
@@ -1208,7 +1306,7 @@ var OpenRouterProvider = class {
1208
1306
  llmInferenceTotal.inc({ status: "success" });
1209
1307
  llmInferenceDuration.observe({ model: this.model }, latencyMs / 1e3);
1210
1308
  return {
1211
- analysis,
1309
+ ...parsedAnalysis,
1212
1310
  provider: this.providerName,
1213
1311
  model: this.model,
1214
1312
  latencyMs,
@@ -1258,7 +1356,7 @@ var OpenRouterProvider = class {
1258
1356
  const text = parsed.success ? parsed.data.choices?.[0]?.message?.content ?? "" : "";
1259
1357
  const usage = parsed.success ? parsed.data.usage : void 0;
1260
1358
  return {
1261
- analysis: parseAnalysis(text, correlationId),
1359
+ ...parseLlmText(text, correlationId),
1262
1360
  provider: this.providerName,
1263
1361
  model: toModel,
1264
1362
  latencyMs: Date.now() - startMs,
@@ -35636,6 +35734,8 @@ async function loadSecretsFromSSM() {
35636
35734
  const key = param.Name.replace(`${prefix}/`, "").replaceAll("-", "_").toUpperCase();
35637
35735
  process.env[key] = param.Value;
35638
35736
  }
35737
+ const missing = result.InvalidParameters ?? [];
35738
+ if (missing.length > 0) createLogger().warn({ missingParameters: missing }, "SSM parameters not found");
35639
35739
  } catch (err) {
35640
35740
  createLogger().error({ err }, "Failed to load SSM parameters");
35641
35741
  }
@@ -35657,7 +35757,9 @@ const ConfigSchema = z.object({
35657
35757
  rollbackActionAllowedSlackUserIds: z.array(z.string()).optional(),
35658
35758
  teamsWebhookUrl: z.string().url().optional(),
35659
35759
  lokiUrl: z.string().optional().transform((v) => v === "" ? void 0 : v),
35660
- redisUrl: z.string().url(),
35760
+ dedupStore: z.enum(["dynamodb", "redis"]).default("dynamodb"),
35761
+ dedupTableName: z.string().min(1).optional(),
35762
+ redisUrl: z.string().url().optional(),
35661
35763
  sqsQueueUrl: z.string().url().optional().or(z.literal("")),
35662
35764
  dedupTtlSeconds: z.coerce.number().int().positive().default(300),
35663
35765
  clusterWindowMs: z.coerce.number().int().positive().default(12e4),
@@ -35718,6 +35820,16 @@ const ConfigSchema = z.object({
35718
35820
  });
35719
35821
  }
35720
35822
  }
35823
+ if (data.dedupStore === "dynamodb" && !data.dedupTableName) ctx.addIssue({
35824
+ code: z.ZodIssueCode.custom,
35825
+ path: ["dedupTableName"],
35826
+ message: "[dedupStore: dynamodb] DEDUP_TABLE_NAME is required"
35827
+ });
35828
+ if (data.dedupStore === "redis" && !data.redisUrl) ctx.addIssue({
35829
+ code: z.ZodIssueCode.custom,
35830
+ path: ["redisUrl"],
35831
+ message: "[dedupStore: redis] REDIS_URL is required"
35832
+ });
35721
35833
  });
35722
35834
  async function loadConfig() {
35723
35835
  await loadSecretsFromSSM();
@@ -35732,6 +35844,8 @@ async function loadConfig() {
35732
35844
  teamsWebhookUrl: process.env["TEAMS_WEBHOOK_URL"],
35733
35845
  lokiUrl: process.env["LOKI_URL"],
35734
35846
  redisUrl: process.env["REDIS_URL"],
35847
+ dedupStore: process.env["DEDUP_STORE"],
35848
+ dedupTableName: process.env["DEDUP_TABLE_NAME"],
35735
35849
  sqsQueueUrl: process.env["SQS_QUEUE_URL"],
35736
35850
  dedupTtlSeconds: process.env["DEDUP_TTL_SECONDS"],
35737
35851
  clusterWindowMs: process.env["CLUSTER_WINDOW_MS"],
@@ -35750,6 +35864,6 @@ async function loadConfig() {
35750
35864
  return result.data;
35751
35865
  }
35752
35866
  //#endregion
35753
- export { ALERT_TYPE_LABELS, AlertClusterSchema, AlertStatusSchema, AlertType, AlertmanagerPayloadSchema, CIRCUIT_BREAKER, ChannelRegistry, ClaudeProvider, ClusteringService, Component, ConsoleNotifier, DEDUP_TTL_MS_MULTIPLIER, DEV_SERVER_PORT, FactoryRegistry, Fingerprint, GeminiProvider, HOUR_MS, HTTP_TIMEOUT_MS, InMemoryAlertQueue, InMemoryDeduplicationStore, InMemoryIndexer, IncidentSchema, LLMAnalysisSchema, LLMProviderType, LLM_FALLBACK_DEFAULTS, LLM_MAX_TOKENS, LLM_MODELS, LokiTraceRepository, MockLLMProvider, MockTraceRepository, NoopRollbackActionHandler, NormalizedAlertSchema, NotifyOutcome, OpenSearchIndexer, Outcome, PAYLOAD_DEFAULTS, ProcessIncidentUseCase, RATE_LIMITER, REDIS_KEY_PREFIX, ROLLBACK_ACTION_ID, RedisDeduplicationStore, RoutingNotifier, RuleActionSchema, RuleActionType, RuleConditionSchema, RuleConfigurationSchema, RuleEngine, RuleEvaluationPhase, RuleSchema, RuleSectionSchema, SLACK_API_URL, SQSAlertQueue, SamplingDecision, SeverityLevel, SlackNotifier, Stage, TEAMS_WEBHOOK_TIMEOUT_MS, TeamsNotifier, TeamsNotifierError, URGENCY_EMOJI, UrgencyLevelSchema, WEBHOOK_DEFAULTS, WideEventBuilder, compileCondition, createLLMProvider, createLogger, createNotifier, createRollbackActionHandler, dispatchActions, flushLoki, loadConfig, metrics_exports as metrics, normalizePayload, parseRuleConfig, reinitLogger, startSqsLagPoller };
35867
+ export { ALERT_TYPE_LABELS, AlertClusterSchema, AlertStatusSchema, AlertType, AlertmanagerPayloadSchema, CIRCUIT_BREAKER, ChannelRegistry, ClaudeProvider, ClusteringService, Component, ConsoleNotifier, DEDUP_TTL_MS_MULTIPLIER, DEV_SERVER_PORT, DynamoDBDeduplicationStore, FactoryRegistry, Fingerprint, GeminiProvider, HOUR_MS, HTTP_TIMEOUT_MS, InMemoryAlertQueue, InMemoryDeduplicationStore, InMemoryIndexer, IncidentSchema, LLMAnalysisSchema, LLMProviderType, LLM_FALLBACK_DEFAULTS, LLM_MAX_TOKENS, LLM_MODELS, LokiTraceRepository, MockLLMProvider, MockTraceRepository, NoopRollbackActionHandler, NormalizedAlertSchema, NotifyOutcome, OpenSearchIndexer, Outcome, PAYLOAD_DEFAULTS, ProcessIncidentUseCase, RATE_LIMITER, REDIS_KEY_PREFIX, ROLLBACK_ACTION_ID, RedisDeduplicationStore, RoutingNotifier, RuleActionSchema, RuleActionType, RuleConditionSchema, RuleConfigurationSchema, RuleEngine, RuleEvaluationPhase, RuleSchema, RuleSectionSchema, SLACK_API_URL, SQSAlertQueue, SamplingDecision, SeverityLevel, SlackNotifier, Stage, TEAMS_WEBHOOK_TIMEOUT_MS, TeamsNotifier, TeamsNotifierError, URGENCY_EMOJI, UrgencyLevelSchema, WEBHOOK_DEFAULTS, WideEventBuilder, compileCondition, createLLMProvider, createLogger, createNotifier, createRollbackActionHandler, dispatchActions, flushLoki, loadConfig, metrics_exports as metrics, normalizePayload, parseRuleConfig, reinitLogger, startSqsLagPoller };
35754
35868
 
35755
35869
  //# sourceMappingURL=index.js.map