@cat-factory/orchestration 0.6.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.
Files changed (182) hide show
  1. package/LICENSE +21 -0
  2. package/dist/container.d.ts +460 -0
  3. package/dist/container.d.ts.map +1 -0
  4. package/dist/container.js +657 -0
  5. package/dist/container.js.map +1 -0
  6. package/dist/index.d.ts +29 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +31 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/modules/board/BoardService.d.ts +125 -0
  11. package/dist/modules/board/BoardService.d.ts.map +1 -0
  12. package/dist/modules/board/BoardService.js +496 -0
  13. package/dist/modules/board/BoardService.js.map +1 -0
  14. package/dist/modules/board/board.logic.d.ts +17 -0
  15. package/dist/modules/board/board.logic.d.ts.map +1 -0
  16. package/dist/modules/board/board.logic.js +51 -0
  17. package/dist/modules/board/board.logic.js.map +1 -0
  18. package/dist/modules/boardScan/BoardScanService.d.ts +35 -0
  19. package/dist/modules/boardScan/BoardScanService.d.ts.map +1 -0
  20. package/dist/modules/boardScan/BoardScanService.js +91 -0
  21. package/dist/modules/boardScan/BoardScanService.js.map +1 -0
  22. package/dist/modules/boardScan/board-scan.logic.d.ts +10 -0
  23. package/dist/modules/boardScan/board-scan.logic.d.ts.map +1 -0
  24. package/dist/modules/boardScan/board-scan.logic.js +26 -0
  25. package/dist/modules/boardScan/board-scan.logic.js.map +1 -0
  26. package/dist/modules/bootstrap/BootstrapService.d.ts +114 -0
  27. package/dist/modules/bootstrap/BootstrapService.d.ts.map +1 -0
  28. package/dist/modules/bootstrap/BootstrapService.js +516 -0
  29. package/dist/modules/bootstrap/BootstrapService.js.map +1 -0
  30. package/dist/modules/clarity/ClarityReviewService.d.ts +48 -0
  31. package/dist/modules/clarity/ClarityReviewService.d.ts.map +1 -0
  32. package/dist/modules/clarity/ClarityReviewService.js +63 -0
  33. package/dist/modules/clarity/ClarityReviewService.js.map +1 -0
  34. package/dist/modules/clarity/clarity.logic.d.ts +36 -0
  35. package/dist/modules/clarity/clarity.logic.d.ts.map +1 -0
  36. package/dist/modules/clarity/clarity.logic.js +98 -0
  37. package/dist/modules/clarity/clarity.logic.js.map +1 -0
  38. package/dist/modules/estimation/estimate.logic.d.ts +11 -0
  39. package/dist/modules/estimation/estimate.logic.d.ts.map +1 -0
  40. package/dist/modules/estimation/estimate.logic.js +37 -0
  41. package/dist/modules/estimation/estimate.logic.js.map +1 -0
  42. package/dist/modules/execution/AgentContextBuilder.d.ts +114 -0
  43. package/dist/modules/execution/AgentContextBuilder.d.ts.map +1 -0
  44. package/dist/modules/execution/AgentContextBuilder.js +316 -0
  45. package/dist/modules/execution/AgentContextBuilder.js.map +1 -0
  46. package/dist/modules/execution/CompanionController.d.ts +60 -0
  47. package/dist/modules/execution/CompanionController.d.ts.map +1 -0
  48. package/dist/modules/execution/CompanionController.js +216 -0
  49. package/dist/modules/execution/CompanionController.js.map +1 -0
  50. package/dist/modules/execution/ExecutionService.d.ts +874 -0
  51. package/dist/modules/execution/ExecutionService.d.ts.map +1 -0
  52. package/dist/modules/execution/ExecutionService.js +2921 -0
  53. package/dist/modules/execution/ExecutionService.js.map +1 -0
  54. package/dist/modules/execution/MergeResolver.d.ts +34 -0
  55. package/dist/modules/execution/MergeResolver.d.ts.map +1 -0
  56. package/dist/modules/execution/MergeResolver.js +81 -0
  57. package/dist/modules/execution/MergeResolver.js.map +1 -0
  58. package/dist/modules/execution/ReviewGateController.d.ts +163 -0
  59. package/dist/modules/execution/ReviewGateController.d.ts.map +1 -0
  60. package/dist/modules/execution/ReviewGateController.js +251 -0
  61. package/dist/modules/execution/ReviewGateController.js.map +1 -0
  62. package/dist/modules/execution/TesterController.d.ts +61 -0
  63. package/dist/modules/execution/TesterController.d.ts.map +1 -0
  64. package/dist/modules/execution/TesterController.js +215 -0
  65. package/dist/modules/execution/TesterController.js.map +1 -0
  66. package/dist/modules/execution/advance.d.ts +84 -0
  67. package/dist/modules/execution/advance.d.ts.map +1 -0
  68. package/dist/modules/execution/advance.js +2 -0
  69. package/dist/modules/execution/advance.js.map +1 -0
  70. package/dist/modules/execution/artifact-review.logic.d.ts +25 -0
  71. package/dist/modules/execution/artifact-review.logic.d.ts.map +1 -0
  72. package/dist/modules/execution/artifact-review.logic.js +39 -0
  73. package/dist/modules/execution/artifact-review.logic.js.map +1 -0
  74. package/dist/modules/execution/ci.logic.d.ts +101 -0
  75. package/dist/modules/execution/ci.logic.d.ts.map +1 -0
  76. package/dist/modules/execution/ci.logic.js +117 -0
  77. package/dist/modules/execution/ci.logic.js.map +1 -0
  78. package/dist/modules/execution/drive.d.ts +47 -0
  79. package/dist/modules/execution/drive.d.ts.map +1 -0
  80. package/dist/modules/execution/drive.js +112 -0
  81. package/dist/modules/execution/drive.js.map +1 -0
  82. package/dist/modules/execution/gates.d.ts +97 -0
  83. package/dist/modules/execution/gates.d.ts.map +1 -0
  84. package/dist/modules/execution/gates.js +2 -0
  85. package/dist/modules/execution/gates.js.map +1 -0
  86. package/dist/modules/execution/individualVendors.logic.d.ts +22 -0
  87. package/dist/modules/execution/individualVendors.logic.d.ts.map +1 -0
  88. package/dist/modules/execution/individualVendors.logic.js +33 -0
  89. package/dist/modules/execution/individualVendors.logic.js.map +1 -0
  90. package/dist/modules/execution/job.logic.d.ts +52 -0
  91. package/dist/modules/execution/job.logic.d.ts.map +1 -0
  92. package/dist/modules/execution/job.logic.js +56 -0
  93. package/dist/modules/execution/job.logic.js.map +1 -0
  94. package/dist/modules/execution/release.logic.d.ts +43 -0
  95. package/dist/modules/execution/release.logic.d.ts.map +1 -0
  96. package/dist/modules/execution/release.logic.js +49 -0
  97. package/dist/modules/execution/release.logic.js.map +1 -0
  98. package/dist/modules/execution/retry.logic.d.ts +40 -0
  99. package/dist/modules/execution/retry.logic.d.ts.map +1 -0
  100. package/dist/modules/execution/retry.logic.js +83 -0
  101. package/dist/modules/execution/retry.logic.js.map +1 -0
  102. package/dist/modules/execution/stepGating.logic.d.ts +15 -0
  103. package/dist/modules/execution/stepGating.logic.d.ts.map +1 -0
  104. package/dist/modules/execution/stepGating.logic.js +29 -0
  105. package/dist/modules/execution/stepGating.logic.js.map +1 -0
  106. package/dist/modules/execution/stepResolvers.d.ts +41 -0
  107. package/dist/modules/execution/stepResolvers.d.ts.map +1 -0
  108. package/dist/modules/execution/stepResolvers.js +2 -0
  109. package/dist/modules/execution/stepResolvers.js.map +1 -0
  110. package/dist/modules/execution/tester-infra.logic.d.ts +42 -0
  111. package/dist/modules/execution/tester-infra.logic.d.ts.map +1 -0
  112. package/dist/modules/execution/tester-infra.logic.js +46 -0
  113. package/dist/modules/execution/tester-infra.logic.js.map +1 -0
  114. package/dist/modules/merge/MergePresetService.d.ts +32 -0
  115. package/dist/modules/merge/MergePresetService.d.ts.map +1 -0
  116. package/dist/modules/merge/MergePresetService.js +109 -0
  117. package/dist/modules/merge/MergePresetService.js.map +1 -0
  118. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts +22 -0
  119. package/dist/modules/modelDefaults/ModelDefaultsService.d.ts.map +1 -0
  120. package/dist/modules/modelDefaults/ModelDefaultsService.js +28 -0
  121. package/dist/modules/modelDefaults/ModelDefaultsService.js.map +1 -0
  122. package/dist/modules/notifications/NotificationService.d.ts +74 -0
  123. package/dist/modules/notifications/NotificationService.d.ts.map +1 -0
  124. package/dist/modules/notifications/NotificationService.js +131 -0
  125. package/dist/modules/notifications/NotificationService.js.map +1 -0
  126. package/dist/modules/observability/LlmObservabilityService.d.ts +121 -0
  127. package/dist/modules/observability/LlmObservabilityService.d.ts.map +1 -0
  128. package/dist/modules/observability/LlmObservabilityService.js +140 -0
  129. package/dist/modules/observability/LlmObservabilityService.js.map +1 -0
  130. package/dist/modules/observability/observability.logic.d.ts +57 -0
  131. package/dist/modules/observability/observability.logic.d.ts.map +1 -0
  132. package/dist/modules/observability/observability.logic.js +186 -0
  133. package/dist/modules/observability/observability.logic.js.map +1 -0
  134. package/dist/modules/pipelines/PipelineService.d.ts +54 -0
  135. package/dist/modules/pipelines/PipelineService.d.ts.map +1 -0
  136. package/dist/modules/pipelines/PipelineService.js +226 -0
  137. package/dist/modules/pipelines/PipelineService.js.map +1 -0
  138. package/dist/modules/pipelines/pipelineShape.d.ts +53 -0
  139. package/dist/modules/pipelines/pipelineShape.d.ts.map +1 -0
  140. package/dist/modules/pipelines/pipelineShape.js +74 -0
  141. package/dist/modules/pipelines/pipelineShape.js.map +1 -0
  142. package/dist/modules/recurring/RecurringPipelineService.d.ts +76 -0
  143. package/dist/modules/recurring/RecurringPipelineService.d.ts.map +1 -0
  144. package/dist/modules/recurring/RecurringPipelineService.js +295 -0
  145. package/dist/modules/recurring/RecurringPipelineService.js.map +1 -0
  146. package/dist/modules/recurring/TrackerSettingsService.d.ts +16 -0
  147. package/dist/modules/recurring/TrackerSettingsService.d.ts.map +1 -0
  148. package/dist/modules/recurring/TrackerSettingsService.js +30 -0
  149. package/dist/modules/recurring/TrackerSettingsService.js.map +1 -0
  150. package/dist/modules/recurring/schedule.logic.d.ts +14 -0
  151. package/dist/modules/recurring/schedule.logic.d.ts.map +1 -0
  152. package/dist/modules/recurring/schedule.logic.js +85 -0
  153. package/dist/modules/recurring/schedule.logic.js.map +1 -0
  154. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts +38 -0
  155. package/dist/modules/releaseHealth/ReleaseHealthService.d.ts.map +1 -0
  156. package/dist/modules/releaseHealth/ReleaseHealthService.js +96 -0
  157. package/dist/modules/releaseHealth/ReleaseHealthService.js.map +1 -0
  158. package/dist/modules/requirements/RequirementReviewService.d.ts +48 -0
  159. package/dist/modules/requirements/RequirementReviewService.d.ts.map +1 -0
  160. package/dist/modules/requirements/RequirementReviewService.js +83 -0
  161. package/dist/modules/requirements/RequirementReviewService.js.map +1 -0
  162. package/dist/modules/requirements/requirements.logic.d.ts +93 -0
  163. package/dist/modules/requirements/requirements.logic.d.ts.map +1 -0
  164. package/dist/modules/requirements/requirements.logic.js +203 -0
  165. package/dist/modules/requirements/requirements.logic.js.map +1 -0
  166. package/dist/modules/review/IterativeReviewService.d.ts +175 -0
  167. package/dist/modules/review/IterativeReviewService.d.ts.map +1 -0
  168. package/dist/modules/review/IterativeReviewService.js +327 -0
  169. package/dist/modules/review/IterativeReviewService.js.map +1 -0
  170. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts +20 -0
  171. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.d.ts.map +1 -0
  172. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js +26 -0
  173. package/dist/modules/serviceFragmentDefaults/ServiceFragmentDefaultsService.js.map +1 -0
  174. package/dist/modules/services/ServiceMountService.d.ts +48 -0
  175. package/dist/modules/services/ServiceMountService.d.ts.map +1 -0
  176. package/dist/modules/services/ServiceMountService.js +90 -0
  177. package/dist/modules/services/ServiceMountService.js.map +1 -0
  178. package/dist/modules/settings/WorkspaceSettingsService.d.ts +22 -0
  179. package/dist/modules/settings/WorkspaceSettingsService.d.ts.map +1 -0
  180. package/dist/modules/settings/WorkspaceSettingsService.js +50 -0
  181. package/dist/modules/settings/WorkspaceSettingsService.js.map +1 -0
  182. package/package.json +41 -0
@@ -0,0 +1,121 @@
1
+ import type { Clock, IdGenerator } from '@cat-factory/kernel';
2
+ import type { LlmCallMetric, LlmCallMetricRepository, LlmCallMetricSummary, LlmTraceSink } from '@cat-factory/kernel';
3
+ import type { LlmMetricsExport } from '@cat-factory/contracts';
4
+ export interface LlmObservabilityServiceDependencies {
5
+ llmCallMetricRepository: LlmCallMetricRepository;
6
+ idGenerator: IdGenerator;
7
+ clock: Clock;
8
+ /**
9
+ * Whether to persist the full prompt body with each metric. Defaults to true. When
10
+ * false, every numeric field (tokens, timing, finish reason, message/tool counts)
11
+ * is still recorded but the prompt is stored empty — for deployments that must not
12
+ * retain the complete prompts sent to the model. Governed by `LLM_RECORD_PROMPTS`.
13
+ */
14
+ recordPrompts?: boolean;
15
+ /**
16
+ * Optional external trace sink (e.g. Langfuse). When wired, every recorded call is
17
+ * ALSO emitted here as a generation — the same code path the inline executor's
18
+ * instrumented model provider feeds, so proxied and inline calls land in one place.
19
+ * Fan-out is best-effort and never blocks or breaks the local recording.
20
+ */
21
+ traceSink?: LlmTraceSink;
22
+ }
23
+ /**
24
+ * Defensive upper bound on a stored prompt/response body (characters). Real agent
25
+ * prompts sit far below this; the cap exists only so a pathological body can't blow
26
+ * past the store's per-row/value limit and make the whole metric fail to record
27
+ * (which would drop the call from observability entirely). A truncated-but-recorded
28
+ * body is strictly more useful than a silently dropped one.
29
+ */
30
+ export declare const MAX_BODY_CHARS: number;
31
+ /** Default cap on how many (newest) calls a list/export returns. */
32
+ export declare const DEFAULT_LIST_LIMIT = 1000;
33
+ /**
34
+ * Details of one proxied LLM call, handed in by the LLM proxy. The proxy owns the
35
+ * timing (it wraps the upstream call): {@link totalMs} is the end-to-end time it
36
+ * spent and {@link upstreamMs} the slice waiting on the model — the difference is
37
+ * transport/proxy overhead, derived here so the two can never disagree.
38
+ */
39
+ export interface RecordLlmCallInput {
40
+ /**
41
+ * The call's id. The proxy mints it so the same id is carried on the live `llmCall`
42
+ * activity event AND this persisted row — the drill-down panel keys its lazy body
43
+ * load by it. Optional: when omitted the service mints one (existing callers).
44
+ */
45
+ id?: string;
46
+ workspaceId: string;
47
+ executionId: string | null;
48
+ agentKind: string;
49
+ provider: string;
50
+ model: string;
51
+ streaming: boolean;
52
+ messageCount: number;
53
+ toolCount: number;
54
+ requestMaxTokens: number | null;
55
+ promptTokens: number;
56
+ cachedPromptTokens: number;
57
+ completionTokens: number;
58
+ totalTokens: number;
59
+ finishReason: string | null;
60
+ /** End-to-end time the proxy spent on the call (ms). */
61
+ totalMs: number;
62
+ /** Time spent waiting on the upstream model (ms). */
63
+ upstreamMs: number;
64
+ ok: boolean;
65
+ httpStatus: number | null;
66
+ errorMessage: string | null;
67
+ promptText: string;
68
+ responseText: string;
69
+ /** The model's reasoning/thinking trace, when emitted on a separate channel (else ''). */
70
+ reasoningText: string;
71
+ }
72
+ /**
73
+ * The LLM observability sink. The proxy meters every container-agent model call
74
+ * here; the engine rolls the per-run aggregates onto pipeline steps for the board,
75
+ * and a query endpoint lists the full per-call detail for the drill-down panel. It
76
+ * is the observability sibling of {@link SpendService} (which keeps only billed
77
+ * totals): this keeps the full prompt/response, the output-limit headroom and the
78
+ * transport-vs-execution latency split. Wired only when a metric repository is
79
+ * present, so tests and unconfigured facades are unaffected.
80
+ */
81
+ export declare class LlmObservabilityService {
82
+ private readonly repository;
83
+ private readonly idGenerator;
84
+ private readonly clock;
85
+ private readonly recordPrompts;
86
+ private readonly traceSink?;
87
+ constructor({ llmCallMetricRepository, idGenerator, clock, recordPrompts, traceSink, }: LlmObservabilityServiceDependencies);
88
+ /**
89
+ * Persist one metered call, assigning its id + timestamp and deriving the overhead.
90
+ * When prompt recording is enabled, the prompt is stored as a DELTA against the
91
+ * previous call in the same `(execution, agentKind)` conversation — a container
92
+ * agent re-sends its whole growing history every call, so storing only the new
93
+ * messages collapses ~21× of redundant prompt bytes (see `computeStoredPrompt`). The
94
+ * full prompt is rebuilt on export. The chain-tip lookup is off the response path
95
+ * (the proxy records via `waitUntil`), so the extra read is free of user latency.
96
+ * When prompt recording is disabled (`recordPrompts: false`) the prompt body is
97
+ * stored empty and the chain-tip read is skipped entirely — the numeric telemetry is
98
+ * still recorded.
99
+ */
100
+ record(input: RecordLlmCallInput): Promise<void>;
101
+ /**
102
+ * Resolve this call's prompt to a delta against the chain tip of its
103
+ * `(workspace, execution, agentKind)` conversation (or the full array when it can't
104
+ * be chained). Only reached when prompt recording is enabled.
105
+ */
106
+ private computeStoredPromptForChain;
107
+ /**
108
+ * Calls recorded for a run, newest first (full prompt/response included), capped
109
+ * at {@link DEFAULT_LIST_LIMIT} so a long run can't produce an unbounded payload.
110
+ */
111
+ listByExecution(workspaceId: string, executionId: string, limit?: number): Promise<LlmCallMetric[]>;
112
+ /** Per-agent-kind aggregates for a run, for the board step rollups. */
113
+ summarizeByExecution(workspaceId: string, executionId: string): Promise<LlmCallMetricSummary[]>;
114
+ /**
115
+ * Build the LLM-friendly export for a run: a self-describing JSON bundle (totals +
116
+ * per-agent insights + every call, with derived ratios) meant to be handed to a
117
+ * model for analysis. Stamped with the service clock.
118
+ */
119
+ exportForExecution(workspaceId: string, executionId: string): Promise<LlmMetricsExport>;
120
+ }
121
+ //# sourceMappingURL=LlmObservabilityService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmObservabilityService.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/LlmObservabilityService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EACvB,oBAAoB,EACpB,YAAY,EACb,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAI9D,MAAM,WAAW,mCAAmC;IAClD,uBAAuB,EAAE,uBAAuB,CAAA;IAChD,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ;;;;;OAKG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,YAAY,CAAA;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,QAAa,CAAA;AAQxC,oEAAoE;AACpE,eAAO,MAAM,kBAAkB,OAAO,CAAA;AAKtC;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,OAAO,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,wDAAwD;IACxD,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAA;IAClB,EAAE,EAAE,OAAO,CAAA;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,0FAA0F;IAC1F,aAAa,EAAE,MAAM,CAAA;CACtB;AAED;;;;;;;;GAQG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAyB;IACpD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAc;IAEzC,YAAY,EACV,uBAAuB,EACvB,WAAW,EACX,KAAK,EACL,aAAoB,EACpB,SAAS,GACV,EAAE,mCAAmC,EAMrC;IAED;;;;;;;;;;;OAWG;IACG,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDrD;IAED;;;;OAIG;YACW,2BAA2B;IAYzC;;;OAGG;IACH,eAAe,CACb,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,GAAE,MAA2B,GACjC,OAAO,CAAC,aAAa,EAAE,CAAC,CAE1B;IAED,uEAAuE;IACvE,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAE9F;IAED;;;;OAIG;IACG,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAG5F;CACF"}
@@ -0,0 +1,140 @@
1
+ import { buildLlmMetricsExport, computeStoredPrompt } from './observability.logic.js';
2
+ /**
3
+ * Defensive upper bound on a stored prompt/response body (characters). Real agent
4
+ * prompts sit far below this; the cap exists only so a pathological body can't blow
5
+ * past the store's per-row/value limit and make the whole metric fail to record
6
+ * (which would drop the call from observability entirely). A truncated-but-recorded
7
+ * body is strictly more useful than a silently dropped one.
8
+ */
9
+ export const MAX_BODY_CHARS = 512 * 1024;
10
+ /** Cap a body to {@link MAX_BODY_CHARS}, marking where it was cut. */
11
+ function clampBody(text) {
12
+ if (text.length <= MAX_BODY_CHARS)
13
+ return text;
14
+ return `${text.slice(0, MAX_BODY_CHARS)}\n…[truncated ${text.length - MAX_BODY_CHARS} chars]`;
15
+ }
16
+ /** Default cap on how many (newest) calls a list/export returns. */
17
+ export const DEFAULT_LIST_LIMIT = 1000;
18
+ /** What to store for a call's prompt when prompt recording is turned off: nothing. */
19
+ const EMPTY_STORED_PROMPT = { promptText: '', promptPrefixCount: 0, promptHash: '' };
20
+ /**
21
+ * The LLM observability sink. The proxy meters every container-agent model call
22
+ * here; the engine rolls the per-run aggregates onto pipeline steps for the board,
23
+ * and a query endpoint lists the full per-call detail for the drill-down panel. It
24
+ * is the observability sibling of {@link SpendService} (which keeps only billed
25
+ * totals): this keeps the full prompt/response, the output-limit headroom and the
26
+ * transport-vs-execution latency split. Wired only when a metric repository is
27
+ * present, so tests and unconfigured facades are unaffected.
28
+ */
29
+ export class LlmObservabilityService {
30
+ repository;
31
+ idGenerator;
32
+ clock;
33
+ recordPrompts;
34
+ traceSink;
35
+ constructor({ llmCallMetricRepository, idGenerator, clock, recordPrompts = true, traceSink, }) {
36
+ this.repository = llmCallMetricRepository;
37
+ this.idGenerator = idGenerator;
38
+ this.clock = clock;
39
+ this.recordPrompts = recordPrompts;
40
+ this.traceSink = traceSink;
41
+ }
42
+ /**
43
+ * Persist one metered call, assigning its id + timestamp and deriving the overhead.
44
+ * When prompt recording is enabled, the prompt is stored as a DELTA against the
45
+ * previous call in the same `(execution, agentKind)` conversation — a container
46
+ * agent re-sends its whole growing history every call, so storing only the new
47
+ * messages collapses ~21× of redundant prompt bytes (see `computeStoredPrompt`). The
48
+ * full prompt is rebuilt on export. The chain-tip lookup is off the response path
49
+ * (the proxy records via `waitUntil`), so the extra read is free of user latency.
50
+ * When prompt recording is disabled (`recordPrompts: false`) the prompt body is
51
+ * stored empty and the chain-tip read is skipped entirely — the numeric telemetry is
52
+ * still recorded.
53
+ */
54
+ async record(input) {
55
+ const overheadMs = Math.max(0, input.totalMs - input.upstreamMs);
56
+ const stored = this.recordPrompts
57
+ ? await this.computeStoredPromptForChain(input)
58
+ : EMPTY_STORED_PROMPT;
59
+ const metric = {
60
+ createdAt: this.clock.now(),
61
+ ...input,
62
+ // Derived/bounded fields last, so they win over any same-named input field.
63
+ // `id` here (not above `...input`) so an absent `input.id` falls back to a mint
64
+ // rather than being spread in as `undefined`.
65
+ id: input.id ?? this.idGenerator.next('llm'),
66
+ overheadMs,
67
+ promptText: clampBody(stored.promptText),
68
+ promptPrefixCount: stored.promptPrefixCount,
69
+ promptHash: stored.promptHash,
70
+ responseText: clampBody(input.responseText),
71
+ reasoningText: clampBody(input.reasoningText),
72
+ };
73
+ await this.repository.record(metric);
74
+ // Fan out to the external trace sink (Langfuse), if wired. We send the FULL prompt
75
+ // (not the stored delta) so the trace is self-contained, honouring the same
76
+ // `recordPrompts` privacy switch as the local store. Best-effort and NON-blocking:
77
+ // dispatched without awaiting (like the inline feeder) so the sink's network round
78
+ // trip never extends the metering path, and isolated so a sink failure can't break
79
+ // local recording. The sink itself swallows + logs and bounds its own request.
80
+ if (this.traceSink) {
81
+ const endedAt = metric.createdAt;
82
+ try {
83
+ void Promise.resolve(this.traceSink.recordGeneration({
84
+ workspaceId: input.workspaceId,
85
+ executionId: input.executionId,
86
+ agentKind: input.agentKind,
87
+ provider: input.provider,
88
+ model: input.model,
89
+ startedAt: Math.max(0, endedAt - input.upstreamMs),
90
+ endedAt,
91
+ promptTokens: input.promptTokens,
92
+ completionTokens: input.completionTokens,
93
+ totalTokens: input.totalTokens,
94
+ finishReason: input.finishReason,
95
+ ok: input.ok,
96
+ errorMessage: input.errorMessage,
97
+ input: this.recordPrompts ? input.promptText : '',
98
+ // Fall back to the reasoning trace when the turn produced no response text
99
+ // (a thinking model that spent its budget reasoning) so the trace isn't blank.
100
+ output: this.recordPrompts ? input.responseText || input.reasoningText : '',
101
+ })).catch(() => { });
102
+ }
103
+ catch {
104
+ // Swallowed: the sink itself logs; observability never breaks the proxy.
105
+ }
106
+ }
107
+ }
108
+ /**
109
+ * Resolve this call's prompt to a delta against the chain tip of its
110
+ * `(workspace, execution, agentKind)` conversation (or the full array when it can't
111
+ * be chained). Only reached when prompt recording is enabled.
112
+ */
113
+ async computeStoredPromptForChain(input) {
114
+ const prev = input.executionId != null
115
+ ? await this.repository.latestChainTip(input.workspaceId, input.executionId, input.agentKind)
116
+ : null;
117
+ return computeStoredPrompt(input.promptText, prev);
118
+ }
119
+ /**
120
+ * Calls recorded for a run, newest first (full prompt/response included), capped
121
+ * at {@link DEFAULT_LIST_LIMIT} so a long run can't produce an unbounded payload.
122
+ */
123
+ listByExecution(workspaceId, executionId, limit = DEFAULT_LIST_LIMIT) {
124
+ return this.repository.listByExecution(workspaceId, executionId, limit);
125
+ }
126
+ /** Per-agent-kind aggregates for a run, for the board step rollups. */
127
+ summarizeByExecution(workspaceId, executionId) {
128
+ return this.repository.summarizeByExecution(workspaceId, executionId);
129
+ }
130
+ /**
131
+ * Build the LLM-friendly export for a run: a self-describing JSON bundle (totals +
132
+ * per-agent insights + every call, with derived ratios) meant to be handed to a
133
+ * model for analysis. Stamped with the service clock.
134
+ */
135
+ async exportForExecution(workspaceId, executionId) {
136
+ const calls = await this.listByExecution(workspaceId, executionId);
137
+ return buildLlmMetricsExport(executionId, calls, this.clock.now());
138
+ }
139
+ }
140
+ //# sourceMappingURL=LlmObservabilityService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LlmObservabilityService.js","sourceRoot":"","sources":["../../../src/modules/observability/LlmObservabilityService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import { type LlmCallMetric } from '@cat-factory/kernel';
2
+ import type { LlmMetricsExport } from '@cat-factory/contracts';
3
+ export type LlmCallOutcome = 'ok' | 'warning' | 'error';
4
+ /** Whether a finish reason is a (non-fatal) warning — output truncated or filtered. */
5
+ export declare function isWarningFinishReason(finishReason: string | null): boolean;
6
+ /**
7
+ * Classify a recorded call: a non-2xx/failed call is an `error`; a successful call
8
+ * cut short by the output limit or content filter is a `warning`; otherwise `ok`.
9
+ */
10
+ export declare function classifyCall(metric: Pick<LlmCallMetric, 'ok' | 'finishReason'>): LlmCallOutcome;
11
+ /**
12
+ * Fraction (0..1) of the output budget the largest single completion consumed, or
13
+ * null when the ceiling is unknown. 1 (or a `length` finish) means a call was
14
+ * truncated. Drives the board's "output-limit headroom" bar.
15
+ */
16
+ export declare function outputHeadroomRatio(peakCompletionTokens: number, maxOutputTokens: number | null): number | null;
17
+ /** Share of total latency spent in transport/proxy overhead (0..1), or null when no timing. */
18
+ export declare function transportOverheadRatio(upstreamMs: number, overheadMs: number): number | null;
19
+ /** The previous call's chain tip: enough to decide if the next call extends it. */
20
+ export interface PromptChainTip {
21
+ /** The previous call's full message count (its `messageCount`). */
22
+ messageCount: number;
23
+ /** The previous call's {@link LlmCallMetric.promptHash} (hash of its full array). */
24
+ promptHash: string;
25
+ }
26
+ /** What to store for a call's prompt after delta compression. */
27
+ export interface StoredPrompt {
28
+ promptText: string;
29
+ promptPrefixCount: number;
30
+ promptHash: string;
31
+ }
32
+ /** Fast, stable, dependency-free hash (FNV-1a, length-salted) for chain validation. */
33
+ export declare function hashPrompt(text: string): string;
34
+ /**
35
+ * Compute what to store for a call's prompt: the delta (new messages only) when this
36
+ * call provably extends the previous one in its chain, else the full array. The
37
+ * returned {@link StoredPrompt.promptHash} is always over the FULL array so the next
38
+ * call can chain onto this one. `fullPromptText` is the proxy's `JSON.stringify(messages)`.
39
+ */
40
+ export declare function computeStoredPrompt(fullPromptText: string, prev: PromptChainTip | null): StoredPrompt;
41
+ /**
42
+ * Rebuild each call's FULL prompt from the stored deltas. Calls are grouped by agent
43
+ * kind (each kind is its own conversation chain) and replayed oldest-first,
44
+ * accumulating the running message array; a call with `promptPrefixCount === 0` resets
45
+ * the chain. Best-effort: if a chain's head is missing (e.g. truncated by a list
46
+ * limit) a delta that can't be rebuilt is returned as-is. The returned calls preserve
47
+ * the input order, with `promptText` set to the full array and `promptPrefixCount` 0.
48
+ */
49
+ export declare function reconstructPrompts(calls: LlmCallMetric[]): LlmCallMetric[];
50
+ /**
51
+ * Build the LLM-friendly export bundle for a run from its recorded calls: a
52
+ * self-describing JSON document (totals + per-agent insights + every call, with
53
+ * derived ratios precomputed) meant to be handed straight to a model for analysis.
54
+ * Pure so it is unit-testable; `generatedAt` is injected (no clock here).
55
+ */
56
+ export declare function buildLlmMetricsExport(executionId: string, storedCalls: LlmCallMetric[], generatedAt: number): LlmMetricsExport;
57
+ //# sourceMappingURL=observability.logic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.logic.d.ts","sourceRoot":"","sources":["../../../src/modules/observability/observability.logic.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACpF,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAMhF,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAAA;AAEvD,uFAAuF;AACvF,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAI1E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,cAAc,CAAC,GAAG,cAAc,CAI/F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,oBAAoB,EAAE,MAAM,EAC5B,eAAe,EAAE,MAAM,GAAG,IAAI,GAC7B,MAAM,GAAG,IAAI,CAGf;AAED,+FAA+F;AAC/F,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG5F;AAkBD,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,mEAAmE;IACnE,YAAY,EAAE,MAAM,CAAA;IACpB,qFAAqF;IACrF,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAA;IAClB,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,uFAAuF;AACvF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAS/C;AAWD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,IAAI,EAAE,cAAc,GAAG,IAAI,GAC1B,YAAY,CAkBd;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAgC1E;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,aAAa,EAAE,EAC5B,WAAW,EAAE,MAAM,GAClB,gBAAgB,CAwDlB"}
@@ -0,0 +1,186 @@
1
+ import { LLM_WARNING_FINISH_REASONS } from '@cat-factory/kernel';
2
+ /** Whether a finish reason is a (non-fatal) warning — output truncated or filtered. */
3
+ export function isWarningFinishReason(finishReason) {
4
+ return (finishReason != null && LLM_WARNING_FINISH_REASONS.includes(finishReason));
5
+ }
6
+ /**
7
+ * Classify a recorded call: a non-2xx/failed call is an `error`; a successful call
8
+ * cut short by the output limit or content filter is a `warning`; otherwise `ok`.
9
+ */
10
+ export function classifyCall(metric) {
11
+ if (!metric.ok)
12
+ return 'error';
13
+ if (isWarningFinishReason(metric.finishReason))
14
+ return 'warning';
15
+ return 'ok';
16
+ }
17
+ /**
18
+ * Fraction (0..1) of the output budget the largest single completion consumed, or
19
+ * null when the ceiling is unknown. 1 (or a `length` finish) means a call was
20
+ * truncated. Drives the board's "output-limit headroom" bar.
21
+ */
22
+ export function outputHeadroomRatio(peakCompletionTokens, maxOutputTokens) {
23
+ if (maxOutputTokens == null || maxOutputTokens <= 0)
24
+ return null;
25
+ return Math.min(1, peakCompletionTokens / maxOutputTokens);
26
+ }
27
+ /** Share of total latency spent in transport/proxy overhead (0..1), or null when no timing. */
28
+ export function transportOverheadRatio(upstreamMs, overheadMs) {
29
+ const total = upstreamMs + overheadMs;
30
+ return total > 0 ? overheadMs / total : null;
31
+ }
32
+ /** Fast, stable, dependency-free hash (FNV-1a, length-salted) for chain validation. */
33
+ export function hashPrompt(text) {
34
+ let h = 0x811c9dc5;
35
+ for (let i = 0; i < text.length; i++) {
36
+ h ^= text.charCodeAt(i);
37
+ // FNV prime, kept in 32-bit range via Math.imul.
38
+ h = Math.imul(h, 0x01000193);
39
+ }
40
+ // Salt with the length so two same-hash-different-length strings can't collide.
41
+ return `${text.length.toString(36)}:${(h >>> 0).toString(36)}`;
42
+ }
43
+ function parseMessages(promptText) {
44
+ try {
45
+ const parsed = JSON.parse(promptText);
46
+ return Array.isArray(parsed) ? parsed : null;
47
+ }
48
+ catch {
49
+ return null;
50
+ }
51
+ }
52
+ /**
53
+ * Compute what to store for a call's prompt: the delta (new messages only) when this
54
+ * call provably extends the previous one in its chain, else the full array. The
55
+ * returned {@link StoredPrompt.promptHash} is always over the FULL array so the next
56
+ * call can chain onto this one. `fullPromptText` is the proxy's `JSON.stringify(messages)`.
57
+ */
58
+ export function computeStoredPrompt(fullPromptText, prev) {
59
+ const promptHash = hashPrompt(fullPromptText);
60
+ const full = parseMessages(fullPromptText);
61
+ // No previous tip, unparseable, or not actually longer ⇒ store the full array.
62
+ if (!full || !prev || prev.messageCount <= 0 || full.length < prev.messageCount) {
63
+ return { promptText: fullPromptText, promptPrefixCount: 0, promptHash };
64
+ }
65
+ // Only elide when the leading `prev.messageCount` messages match what the previous
66
+ // call stored (append-only). A mismatch (fresh conversation / compaction) ⇒ full.
67
+ const prefix = JSON.stringify(full.slice(0, prev.messageCount));
68
+ if (hashPrompt(prefix) !== prev.promptHash) {
69
+ return { promptText: fullPromptText, promptPrefixCount: 0, promptHash };
70
+ }
71
+ return {
72
+ promptText: JSON.stringify(full.slice(prev.messageCount)),
73
+ promptPrefixCount: prev.messageCount,
74
+ promptHash,
75
+ };
76
+ }
77
+ /**
78
+ * Rebuild each call's FULL prompt from the stored deltas. Calls are grouped by agent
79
+ * kind (each kind is its own conversation chain) and replayed oldest-first,
80
+ * accumulating the running message array; a call with `promptPrefixCount === 0` resets
81
+ * the chain. Best-effort: if a chain's head is missing (e.g. truncated by a list
82
+ * limit) a delta that can't be rebuilt is returned as-is. The returned calls preserve
83
+ * the input order, with `promptText` set to the full array and `promptPrefixCount` 0.
84
+ */
85
+ export function reconstructPrompts(calls) {
86
+ // Order by time, then message count, then id. Records are written off the response
87
+ // path (`waitUntil`) so two calls can share a `createdAt` millisecond; `messageCount`
88
+ // is monotonic within an append-only chain, so it breaks such ties in true
89
+ // conversation order (id is the last resort when even that is equal).
90
+ const asc = [...calls].sort((a, b) => a.createdAt - b.createdAt || a.messageCount - b.messageCount || a.id.localeCompare(b.id));
91
+ const running = new Map();
92
+ const fullById = new Map();
93
+ for (const c of asc) {
94
+ const delta = parseMessages(c.promptText) ?? [];
95
+ let full;
96
+ if (c.promptPrefixCount > 0) {
97
+ const prev = running.get(c.agentKind) ?? [];
98
+ // Only rebuild when we actually hold the referenced prefix; else keep the delta.
99
+ full =
100
+ prev.length >= c.promptPrefixCount
101
+ ? [...prev.slice(0, c.promptPrefixCount), ...delta]
102
+ : delta;
103
+ }
104
+ else {
105
+ full = delta;
106
+ }
107
+ running.set(c.agentKind, full);
108
+ fullById.set(c.id, JSON.stringify(full));
109
+ }
110
+ return calls.map((c) => ({
111
+ ...c,
112
+ promptText: fullById.get(c.id) ?? c.promptText,
113
+ promptPrefixCount: 0,
114
+ }));
115
+ }
116
+ /**
117
+ * Build the LLM-friendly export bundle for a run from its recorded calls: a
118
+ * self-describing JSON document (totals + per-agent insights + every call, with
119
+ * derived ratios precomputed) meant to be handed straight to a model for analysis.
120
+ * Pure so it is unit-testable; `generatedAt` is injected (no clock here).
121
+ */
122
+ export function buildLlmMetricsExport(executionId, storedCalls, generatedAt) {
123
+ // The export is a self-contained analysis bundle, so rebuild each call's full
124
+ // prompt from the stored deltas before assembling it.
125
+ const calls = reconstructPrompts(storedCalls);
126
+ const byKind = new Map();
127
+ for (const call of calls) {
128
+ const list = byKind.get(call.agentKind);
129
+ if (list)
130
+ list.push(call);
131
+ else
132
+ byKind.set(call.agentKind, [call]);
133
+ }
134
+ const insights = [...byKind.entries()].map(([agentKind, kindCalls]) => {
135
+ const promptTokens = sum(kindCalls, (c) => c.promptTokens);
136
+ const completionTokens = sum(kindCalls, (c) => c.completionTokens);
137
+ const peakCompletionTokens = kindCalls.reduce((m, c) => Math.max(m, c.completionTokens), 0);
138
+ const maxOutputTokens = maxNullable(kindCalls.map((c) => c.requestMaxTokens));
139
+ const upstreamMs = sum(kindCalls, (c) => c.upstreamMs);
140
+ const overheadMs = sum(kindCalls, (c) => c.overheadMs);
141
+ return {
142
+ agentKind,
143
+ calls: kindCalls.length,
144
+ promptTokens,
145
+ completionTokens,
146
+ peakCompletionTokens,
147
+ maxOutputTokens,
148
+ outputHeadroomRatio: outputHeadroomRatio(peakCompletionTokens, maxOutputTokens),
149
+ truncatedCalls: kindCalls.filter((c) => c.finishReason === 'length').length,
150
+ upstreamMs,
151
+ overheadMs,
152
+ transportOverheadRatio: transportOverheadRatio(upstreamMs, overheadMs),
153
+ errors: kindCalls.filter((c) => !c.ok).length,
154
+ warnings: kindCalls.filter((c) => c.ok && isWarningFinishReason(c.finishReason)).length,
155
+ };
156
+ });
157
+ const upstreamMs = sum(calls, (c) => c.upstreamMs);
158
+ const overheadMs = sum(calls, (c) => c.overheadMs);
159
+ return {
160
+ kind: 'cat-factory.llm-metrics-export',
161
+ version: 1,
162
+ executionId,
163
+ generatedAt,
164
+ totals: {
165
+ calls: calls.length,
166
+ promptTokens: sum(calls, (c) => c.promptTokens),
167
+ completionTokens: sum(calls, (c) => c.completionTokens),
168
+ upstreamMs,
169
+ overheadMs,
170
+ transportOverheadRatio: transportOverheadRatio(upstreamMs, overheadMs),
171
+ errors: calls.filter((c) => !c.ok).length,
172
+ warnings: calls.filter((c) => c.ok && isWarningFinishReason(c.finishReason)).length,
173
+ truncatedCalls: calls.filter((c) => c.finishReason === 'length').length,
174
+ },
175
+ insights,
176
+ calls,
177
+ };
178
+ }
179
+ function sum(items, pick) {
180
+ return items.reduce((acc, item) => acc + pick(item), 0);
181
+ }
182
+ function maxNullable(values) {
183
+ const present = values.filter((v) => v != null);
184
+ return present.length > 0 ? Math.max(...present) : null;
185
+ }
186
+ //# sourceMappingURL=observability.logic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.logic.js","sourceRoot":"","sources":["../../../src/modules/observability/observability.logic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { ClonePipelineInput, CreatePipelineInput, OrganizePipelineInput, UpdatePipelineInput } from '@cat-factory/contracts';
2
+ import type { Pipeline } from '@cat-factory/kernel';
3
+ import type { DatadogConnectionRepository, PipelineRepository, WorkspaceRepository } from '@cat-factory/kernel';
4
+ import type { IdGenerator } from '@cat-factory/kernel';
5
+ export interface PipelineServiceDependencies {
6
+ workspaceRepository: WorkspaceRepository;
7
+ pipelineRepository: PipelineRepository;
8
+ idGenerator: IdGenerator;
9
+ /**
10
+ * Resolves whether the workspace has any observability integration enabled (today: a
11
+ * Datadog connection). When absent (no observability persistence wired at all), the
12
+ * observability-gated step can never be added.
13
+ */
14
+ datadogConnectionRepository?: DatadogConnectionRepository;
15
+ }
16
+ /** Saved, reusable pipelines (the pipeline palette). */
17
+ export declare class PipelineService {
18
+ private readonly workspaceRepository;
19
+ private readonly pipelineRepository;
20
+ private readonly idGenerator;
21
+ private readonly datadogConnectionRepository?;
22
+ constructor({ workspaceRepository, pipelineRepository, idGenerator, datadogConnectionRepository, }: PipelineServiceDependencies);
23
+ /**
24
+ * The post-release-health gate is only meaningful with an observability integration, so
25
+ * reject a chain that includes an ENABLED post-release-health step unless the workspace
26
+ * has one wired. Validated only when the chain/enable mask is being authored (create, or
27
+ * an update that changes them) so an unrelated edit to an existing pipeline never trips.
28
+ */
29
+ private assertObservabilityGatedStepAllowed;
30
+ private requireWorkspace;
31
+ list(workspaceId: string): Promise<Pipeline[]>;
32
+ create(workspaceId: string, input: CreatePipelineInput): Promise<Pipeline>;
33
+ /**
34
+ * Clone any pipeline (built-in or custom) into a new, editable copy. The copy keeps
35
+ * the source's steps / gates / thresholds / enable flags but is never `builtin`, so
36
+ * it can be edited — this is how a built-in template is "made editable".
37
+ */
38
+ clone(workspaceId: string, sourceId: string, input: ClonePipelineInput): Promise<Pipeline>;
39
+ /**
40
+ * Edit a custom pipeline in place. Only the supplied fields change; passing
41
+ * `agentKinds` replaces the whole chain and re-aligns the parallel arrays. Built-in
42
+ * catalog templates are read-only and reject this — clone them first.
43
+ */
44
+ update(workspaceId: string, id: string, input: UpdatePipelineInput): Promise<Pipeline>;
45
+ remove(workspaceId: string, id: string): Promise<void>;
46
+ /**
47
+ * Set a pipeline's organizational metadata (labels and/or archive state). This is the
48
+ * ONLY mutation allowed on a BUILT-IN pipeline — it touches the library view, not the
49
+ * pipeline's structure, so a built-in can be tagged or archived while staying read-only
50
+ * for its steps. Only the supplied fields change.
51
+ */
52
+ organize(workspaceId: string, id: string, input: OrganizePipelineInput): Promise<Pipeline>;
53
+ }
54
+ //# sourceMappingURL=PipelineService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PipelineService.d.ts","sourceRoot":"","sources":["../../../src/modules/pipelines/PipelineService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACpB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,KAAK,EAAuB,QAAQ,EAAc,MAAM,qBAAqB,CAAA;AAEpF,OAAO,KAAK,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,qBAAqB,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAWtD,MAAM,WAAW,2BAA2B;IAC1C,mBAAmB,EAAE,mBAAmB,CAAA;IACxC,kBAAkB,EAAE,kBAAkB,CAAA;IACtC,WAAW,EAAE,WAAW,CAAA;IACxB;;;;OAIG;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAA;CAC1D;AAED,wDAAwD;AACxD,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAqB;IACzD,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoB;IACvD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAA6B;IAE1E,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,WAAW,EACX,2BAA2B,GAC5B,EAAE,2BAA2B,EAK7B;IAED;;;;;OAKG;YACW,mCAAmC;IAiBjD,OAAO,CAAC,gBAAgB;IAIlB,IAAI,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAGnD;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAsB/E;IAED;;;;OAIG;IACG,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,QAAQ,CAAC,CA6B/F;IAED;;;;OAIG;IACG,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuC3F;IAEK,MAAM,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS3D;IAED;;;;;OAKG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAc/F;CACF"}