@cat-factory/app 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 (189) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -0
  3. package/app/app.config.ts +8 -0
  4. package/app/app.vue +11 -0
  5. package/app/assets/css/main.css +100 -0
  6. package/app/components/auth/AuthGate.vue +24 -0
  7. package/app/components/auth/LoginScreen.vue +143 -0
  8. package/app/components/auth/UserMenu.vue +39 -0
  9. package/app/components/board/AddTaskModal.vue +444 -0
  10. package/app/components/board/AgentFailureCard.vue +97 -0
  11. package/app/components/board/AgentStopButton.vue +61 -0
  12. package/app/components/board/BoardCanvas.vue +183 -0
  13. package/app/components/board/ContextPicker.vue +367 -0
  14. package/app/components/board/RecurringPipelineModal.vue +219 -0
  15. package/app/components/board/TaskDependencyEdges.vue +132 -0
  16. package/app/components/board/nodes/AgentChip.vue +59 -0
  17. package/app/components/board/nodes/BlockNode.vue +433 -0
  18. package/app/components/board/nodes/DecisionBadge.vue +27 -0
  19. package/app/components/board/nodes/DraggableTask.vue +48 -0
  20. package/app/components/board/nodes/ModuleFrame.vue +97 -0
  21. package/app/components/board/nodes/TaskCard.vue +359 -0
  22. package/app/components/board/nodes/TaskPipelineMini.vue +159 -0
  23. package/app/components/bootstrap/BootstrapModal.vue +665 -0
  24. package/app/components/clarity/ClarityReviewWindow.vue +611 -0
  25. package/app/components/consensus/ConsensusSessionWindow.vue +210 -0
  26. package/app/components/documents/DocumentImportModal.vue +161 -0
  27. package/app/components/documents/DocumentSourceConnectModal.vue +127 -0
  28. package/app/components/documents/SpawnPreviewModal.vue +161 -0
  29. package/app/components/documents/TaskContextDocs.vue +83 -0
  30. package/app/components/focus/BlockFocusView.vue +171 -0
  31. package/app/components/fragments/FragmentLibraryPanel.vue +340 -0
  32. package/app/components/gates/GateResultView.vue +282 -0
  33. package/app/components/github/AddServiceFromRepoModal.vue +354 -0
  34. package/app/components/github/GitHubConnect.vue +183 -0
  35. package/app/components/github/GitHubOnboarding.vue +45 -0
  36. package/app/components/github/GitHubPanel.vue +584 -0
  37. package/app/components/github/RepoTreeBrowser.vue +171 -0
  38. package/app/components/layout/AccountTeamSettings.vue +237 -0
  39. package/app/components/layout/BoardSwitcher.vue +280 -0
  40. package/app/components/layout/BoardToolbar.vue +156 -0
  41. package/app/components/layout/CommandBar.vue +336 -0
  42. package/app/components/layout/GitHubPatBanner.vue +73 -0
  43. package/app/components/layout/NotificationsInbox.vue +175 -0
  44. package/app/components/layout/SideBar.vue +314 -0
  45. package/app/components/layout/SpendWarningBanner.vue +107 -0
  46. package/app/components/observability/StepMetricsBar.vue +102 -0
  47. package/app/components/palettes/AgentPalette.vue +86 -0
  48. package/app/components/panels/AgentStepDetail.vue +737 -0
  49. package/app/components/panels/DecisionModal.vue +71 -0
  50. package/app/components/panels/InspectorPanel.vue +465 -0
  51. package/app/components/panels/ObservabilityPanel.vue +351 -0
  52. package/app/components/panels/StepMetadataCard.vue +253 -0
  53. package/app/components/panels/StepRestartControl.vue +90 -0
  54. package/app/components/panels/StepResultViewHost.vue +40 -0
  55. package/app/components/panels/StepTestReport.vue +84 -0
  56. package/app/components/panels/inspector/ContainerSummary.vue +74 -0
  57. package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -0
  58. package/app/components/panels/inspector/ServiceFragments.vue +82 -0
  59. package/app/components/panels/inspector/ServiceTestConfig.vue +198 -0
  60. package/app/components/panels/inspector/TaskAgentConfig.vue +81 -0
  61. package/app/components/panels/inspector/TaskDependencies.vue +70 -0
  62. package/app/components/panels/inspector/TaskEstimateBadge.vue +56 -0
  63. package/app/components/panels/inspector/TaskExecution.vue +364 -0
  64. package/app/components/panels/inspector/TaskRunSettings.vue +187 -0
  65. package/app/components/panels/inspector/TaskStructure.vue +96 -0
  66. package/app/components/pipeline/AgentKindIcon.vue +30 -0
  67. package/app/components/pipeline/IterationCapPrompt.vue +70 -0
  68. package/app/components/pipeline/PipelineBuilder.vue +817 -0
  69. package/app/components/pipeline/PipelineProgress.vue +484 -0
  70. package/app/components/providers/ApiKeysSection.vue +273 -0
  71. package/app/components/providers/PersonalCredentialModal.vue +128 -0
  72. package/app/components/providers/PersonalSubscriptionSection.vue +225 -0
  73. package/app/components/providers/VendorCredentialsModal.vue +197 -0
  74. package/app/components/recurring/RecurrenceEditor.vue +124 -0
  75. package/app/components/requirements/RequirementsReviewWindow.vue +620 -0
  76. package/app/components/settings/DatadogPanel.vue +213 -0
  77. package/app/components/settings/LocalModelEndpointsPanel.vue +286 -0
  78. package/app/components/settings/MergeThresholdsPanel.vue +378 -0
  79. package/app/components/settings/ModelDefaultsPanel.vue +250 -0
  80. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +124 -0
  81. package/app/components/settings/WorkspaceSettingsPanel.vue +142 -0
  82. package/app/components/slack/SlackPanel.vue +299 -0
  83. package/app/components/tasks/TaskContextIssues.vue +88 -0
  84. package/app/components/tasks/TaskImportModal.vue +207 -0
  85. package/app/components/tasks/TaskSourceConnectModal.vue +133 -0
  86. package/app/components/testing/TestReportWindow.vue +404 -0
  87. package/app/composables/api/accounts.ts +81 -0
  88. package/app/composables/api/auth.ts +45 -0
  89. package/app/composables/api/board.ts +101 -0
  90. package/app/composables/api/bootstrap.ts +62 -0
  91. package/app/composables/api/context.ts +25 -0
  92. package/app/composables/api/documents.ts +74 -0
  93. package/app/composables/api/execution.ts +127 -0
  94. package/app/composables/api/fragments.ts +71 -0
  95. package/app/composables/api/github.ts +131 -0
  96. package/app/composables/api/models.ts +127 -0
  97. package/app/composables/api/notifications.ts +23 -0
  98. package/app/composables/api/presets.ts +29 -0
  99. package/app/composables/api/recurring.ts +68 -0
  100. package/app/composables/api/releaseHealth.ts +43 -0
  101. package/app/composables/api/reviews.ts +146 -0
  102. package/app/composables/api/slack.ts +54 -0
  103. package/app/composables/api/tasks.ts +72 -0
  104. package/app/composables/api/workspaces.ts +36 -0
  105. package/app/composables/useApi.ts +89 -0
  106. package/app/composables/useBlockDrag.ts +90 -0
  107. package/app/composables/useBlockQueries.ts +154 -0
  108. package/app/composables/useBoardFlow.ts +11 -0
  109. package/app/composables/useContextLinking.ts +65 -0
  110. package/app/composables/useDepLabels.ts +26 -0
  111. package/app/composables/useFrameResize.ts +54 -0
  112. package/app/composables/useResultView.ts +48 -0
  113. package/app/composables/useReviewStage.ts +40 -0
  114. package/app/composables/useSemanticZoom.ts +31 -0
  115. package/app/composables/useStepApproval.ts +233 -0
  116. package/app/composables/useStepProse.ts +78 -0
  117. package/app/composables/useStepTimer.ts +63 -0
  118. package/app/composables/useTaskExpansion.ts +92 -0
  119. package/app/composables/useWorkspaceStream.ts +155 -0
  120. package/app/docs/architecture.md +31 -0
  121. package/app/pages/index.vue +141 -0
  122. package/app/stores/accounts.ts +152 -0
  123. package/app/stores/agentConfig.ts +35 -0
  124. package/app/stores/agentRuns.ts +122 -0
  125. package/app/stores/agents.ts +40 -0
  126. package/app/stores/apiKeys.ts +108 -0
  127. package/app/stores/auth.ts +166 -0
  128. package/app/stores/board.spec.ts +205 -0
  129. package/app/stores/board.ts +286 -0
  130. package/app/stores/bootstrap.ts +97 -0
  131. package/app/stores/clarity.ts +196 -0
  132. package/app/stores/consensus.ts +60 -0
  133. package/app/stores/documents.ts +176 -0
  134. package/app/stores/execution.ts +273 -0
  135. package/app/stores/fragmentLibrary.ts +147 -0
  136. package/app/stores/fragments.ts +40 -0
  137. package/app/stores/github.ts +305 -0
  138. package/app/stores/localModels.ts +51 -0
  139. package/app/stores/mergePresets.ts +58 -0
  140. package/app/stores/modelDefaults.ts +76 -0
  141. package/app/stores/models.ts +134 -0
  142. package/app/stores/notifications.ts +70 -0
  143. package/app/stores/observability.ts +144 -0
  144. package/app/stores/personalSubscriptions.ts +215 -0
  145. package/app/stores/pipelines.ts +327 -0
  146. package/app/stores/recurringPipelines.ts +112 -0
  147. package/app/stores/releaseHealth.ts +75 -0
  148. package/app/stores/requirements.spec.ts +94 -0
  149. package/app/stores/requirements.ts +208 -0
  150. package/app/stores/serviceFragmentDefaults.ts +29 -0
  151. package/app/stores/services.ts +87 -0
  152. package/app/stores/slack.ts +142 -0
  153. package/app/stores/taskExpansion.ts +36 -0
  154. package/app/stores/tasks.spec.ts +71 -0
  155. package/app/stores/tasks.ts +176 -0
  156. package/app/stores/tracker.ts +27 -0
  157. package/app/stores/ui.ts +434 -0
  158. package/app/stores/vendorCredentials.ts +54 -0
  159. package/app/stores/workspace.ts +215 -0
  160. package/app/stores/workspaceSettings.ts +36 -0
  161. package/app/types/accounts.ts +77 -0
  162. package/app/types/bootstrap.ts +83 -0
  163. package/app/types/clarity.ts +59 -0
  164. package/app/types/consensus.ts +91 -0
  165. package/app/types/documents.ts +104 -0
  166. package/app/types/domain.ts +495 -0
  167. package/app/types/execution.ts +383 -0
  168. package/app/types/fragments.ts +72 -0
  169. package/app/types/github.ts +173 -0
  170. package/app/types/localModels.ts +73 -0
  171. package/app/types/merge.ts +71 -0
  172. package/app/types/models.ts +157 -0
  173. package/app/types/notifications.ts +74 -0
  174. package/app/types/recurring.ts +69 -0
  175. package/app/types/releaseHealth.ts +31 -0
  176. package/app/types/requirements.ts +61 -0
  177. package/app/types/services.ts +27 -0
  178. package/app/types/slack.ts +57 -0
  179. package/app/types/tasks.ts +82 -0
  180. package/app/types/tracker.ts +18 -0
  181. package/app/utils/agentOutput.spec.ts +128 -0
  182. package/app/utils/agentOutput.ts +173 -0
  183. package/app/utils/catalog.spec.ts +112 -0
  184. package/app/utils/catalog.ts +455 -0
  185. package/app/utils/dnd.ts +29 -0
  186. package/app/utils/observability.ts +52 -0
  187. package/app/utils/pipelineRender.ts +151 -0
  188. package/nuxt.config.ts +55 -0
  189. package/package.json +45 -0
@@ -0,0 +1,383 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Execution model: a pipeline of agents running against a single block.
3
+ // Mirrors the `@cat-factory/contracts` execution schemas.
4
+ // ---------------------------------------------------------------------------
5
+
6
+ import type { AgentKind, TestReport } from './domain'
7
+ import type { ConsensusStepConfig } from './consensus'
8
+
9
+ /**
10
+ * A quality companion's verdict on one producer output — the standardized shape every
11
+ * pipeline companion step stores, one per rework cycle.
12
+ */
13
+ export interface CompanionVerdict {
14
+ /** Overall quality rating of the graded output (0..1, higher = better). */
15
+ rating: number
16
+ /** The quality bar the rating had to reach to pass. */
17
+ threshold: number
18
+ /** Whether the rating met the threshold. */
19
+ passed: boolean
20
+ /** The companion's challenge, shown to the human and fed into the next rework. */
21
+ feedback: string
22
+ }
23
+
24
+ /** Live companion state on a companion step: the bar, the budget, and every verdict. */
25
+ export interface StepCompanion {
26
+ /** the quality bar (0..1) the latest verdict's rating must reach */
27
+ threshold: number
28
+ /** the automatic rework budget: once `attempts` reaches this the gate parks for a human */
29
+ maxAttempts: number
30
+ /** how many AUTOMATIC reworks have run (human "request changes" cycles don't count) */
31
+ attempts?: number
32
+ /** one verdict per correction cycle, in order; the last is the latest */
33
+ verdicts: CompanionVerdict[]
34
+ /**
35
+ * Set once the automatic rework budget is spent with the rating still below the bar:
36
+ * the step parks on its approval gate for a human to resolve via the iteration-cap
37
+ * prompt (one more round / proceed / stop & reset). Cleared on an extra round.
38
+ */
39
+ exceeded?: boolean
40
+ }
41
+
42
+ /**
43
+ * How a human resolves an iterative agent gate that hit its budget — shared by the
44
+ * requirements reviewer and the quality companions. Mirror of the backend's
45
+ * `IterationCapChoice` (see `@cat-factory/contracts` iteration-cap.ts).
46
+ */
47
+ export type IterationCapChoice = 'extra-round' | 'proceed' | 'stop-reset'
48
+
49
+ /** Runtime state of a single agent within a running execution. */
50
+ export type AgentState =
51
+ | 'pending' // not started
52
+ | 'working' // actively (visually) working
53
+ | 'waiting_decision' // paused, needs a human decision
54
+ | 'done' // finished
55
+
56
+ /** A decision an agent surfaces mid-step that a human must resolve. */
57
+ export interface Decision {
58
+ id: string
59
+ question: string
60
+ options: string[]
61
+ chosen: string | null
62
+ }
63
+
64
+ /** One entry of a running step's todo list — its label and current status. */
65
+ export interface StepSubtaskItem {
66
+ label: string
67
+ status: 'pending' | 'in_progress' | 'completed'
68
+ }
69
+
70
+ /** Live subtask counts a running step reports from the agent's own todo list. */
71
+ export interface StepSubtasks {
72
+ completed: number
73
+ inProgress: number
74
+ total: number
75
+ /** The individual todo entries, so a zoomed-in card can show the actual list. */
76
+ items?: StepSubtaskItem[]
77
+ }
78
+
79
+ // ---------------------------------------------------------------------------
80
+ // Shared "agent run" failure model. Both flows that run an agent in a container
81
+ // — a task pipeline `execution` and a repo `bootstrap` — surface failures with
82
+ // this shape, so the board renders one failure banner + retry for either. Mirrors
83
+ // `agentFailureSchema` in `@cat-factory/contracts`.
84
+ // ---------------------------------------------------------------------------
85
+
86
+ /** The agent flows that produce a container-backed "agent run". */
87
+ export type AgentRunKind = 'bootstrap' | 'execution'
88
+
89
+ /** How an agent run faulted, so the board can classify it (and hint at a retry).
90
+ * The union spans both flows; a given flow only ever produces a subset. */
91
+ export type AgentFailureKind =
92
+ | 'preflight'
93
+ | 'dispatch'
94
+ | 'evicted'
95
+ | 'timeout'
96
+ | 'agent'
97
+ | 'job_failed'
98
+ | 'rejected'
99
+ | 'cancelled'
100
+ | 'unknown'
101
+
102
+ /** Structured diagnostics captured when an agent run fails. */
103
+ export interface AgentFailure {
104
+ kind: AgentFailureKind
105
+ /** Human-readable summary (mirrors the run's one-line `error`). */
106
+ message: string
107
+ /** Extended detail when available (the harness's reason, an HTTP body, …). */
108
+ detail: string | null
109
+ /** Where to look next (e.g. "check the container logs for this job id"). */
110
+ hint: string | null
111
+ /** Epoch ms the failure was recorded. */
112
+ occurredAt: number
113
+ /** Last subtask counts seen before the failure, for context (null if none). */
114
+ lastSubtasks: StepSubtasks | null
115
+ }
116
+
117
+ /**
118
+ * One GitHub-review-style comment on a block of an agent's proposal (mirrors
119
+ * `stepReviewCommentSchema` in contracts). `quotedSource` is the verbatim raw
120
+ * markdown of the commented block, so a "request changes" re-run quotes the
121
+ * agent's own text back to it.
122
+ */
123
+ export interface ReviewComment {
124
+ quotedSource: string
125
+ /** 0-based source line range [start, end) of the commented block. */
126
+ srcStart: number
127
+ srcEnd: number
128
+ body: string
129
+ }
130
+
131
+ /**
132
+ * A human approval gate on a step (mirrors `stepApprovalSchema` in contracts).
133
+ * Raised once a gated step's proposal is ready; the human reviews it in the
134
+ * conclusions reader, then approves (advance), requests changes (re-run with
135
+ * freeform feedback + per-block comments) or rejects (stop the run).
136
+ */
137
+ export interface StepApproval {
138
+ id: string
139
+ status: 'pending' | 'approved' | 'changes_requested' | 'rejected'
140
+ /** the agent's output the human reviews */
141
+ proposal: string
142
+ /** the human's freeform guidance when changes were requested */
143
+ feedback?: string
144
+ /** per-block review comments when changes were requested */
145
+ comments?: ReviewComment[]
146
+ }
147
+
148
+ /**
149
+ * LLM observability rollup for a step (mirrors `stepMetricsSchema` in contracts):
150
+ * a compact aggregate over every model call the step's container made — token
151
+ * usage, how close it ran to the output-token limit (truncation), the latency
152
+ * split between transport/proxy overhead and actual model execution, and any
153
+ * errors/warnings. Absent when the observability sink is not wired.
154
+ */
155
+ export interface StepMetrics {
156
+ calls: number
157
+ promptTokens: number
158
+ completionTokens: number
159
+ /** the largest single completion produced (closest approach to the limit) */
160
+ peakCompletionTokens: number
161
+ /** the output ceiling in effect (max requested max_tokens), or null when unknown */
162
+ maxOutputTokens: number | null
163
+ /** calls cut short by the output limit (finish_reason === 'length') */
164
+ truncatedCalls: number
165
+ /** sum of model execution time (ms) — the actual prompt/tool execution */
166
+ upstreamMs: number
167
+ /** sum of transport/proxy overhead (ms) — the interim-layer cost */
168
+ overheadMs: number
169
+ /** calls that failed (non-2xx / refused / in-process error) */
170
+ errors: number
171
+ /** successful calls that warned (truncated or content-filtered) */
172
+ warnings: number
173
+ }
174
+
175
+ /** One proxied LLM call's full detail (mirrors `llmCallMetricSchema` in contracts). */
176
+ export interface LlmCallMetric {
177
+ id: string
178
+ workspaceId: string
179
+ executionId: string | null
180
+ agentKind: string
181
+ provider: string
182
+ model: string
183
+ createdAt: number
184
+ streaming: boolean
185
+ messageCount: number
186
+ toolCount: number
187
+ requestMaxTokens: number | null
188
+ promptTokens: number
189
+ /** prompt tokens served from the provider's prompt cache (subset of promptTokens) */
190
+ cachedPromptTokens: number
191
+ completionTokens: number
192
+ totalTokens: number
193
+ finishReason: string | null
194
+ upstreamMs: number
195
+ overheadMs: number
196
+ totalMs: number
197
+ ok: boolean
198
+ httpStatus: number | null
199
+ errorMessage: string | null
200
+ /**
201
+ * the request messages serialised as JSON, stored as a delta — only the messages
202
+ * this call appended beyond `promptPrefixCount` (the full array when that is 0)
203
+ */
204
+ promptText: string
205
+ /** leading messages elided from `promptText` (0 ⇒ it is the full array) */
206
+ promptPrefixCount: number
207
+ /** hash of the call's full messages array (chain key for the next call's delta) */
208
+ promptHash: string
209
+ /** the full assistant response text */
210
+ responseText: string
211
+ /**
212
+ * the model's reasoning/"thinking" trace on a separate channel, when emitted (empty
213
+ * for non-reasoning models). a thinking model can spend its whole output budget here
214
+ * and return empty `responseText`.
215
+ */
216
+ reasoningText: string
217
+ }
218
+
219
+ /**
220
+ * The compact per-call summary pushed live over the workspace stream (the `llmCall`
221
+ * event). It is {@link LlmCallMetric} WITHOUT the heavy text bodies and delta
222
+ * bookkeeping, so an open "Model activity" panel updates in real time without
223
+ * shipping prompt bytes. The panel lazy-loads the full bodies for an expanded row
224
+ * from the persisted metrics endpoint, keyed by the shared `id`. Mirrors
225
+ * `LlmCallActivity` in `@cat-factory/contracts`.
226
+ */
227
+ export type LlmCallActivity = Omit<
228
+ LlmCallMetric,
229
+ 'promptText' | 'responseText' | 'reasoningText' | 'promptPrefixCount' | 'promptHash'
230
+ >
231
+
232
+ /** One per-agent-kind insight in the LLM-friendly export (rollup + derived ratios). */
233
+ export interface LlmExportInsight extends StepMetrics {
234
+ agentKind: string
235
+ /** peakCompletionTokens / maxOutputTokens, 0..1; null when the ceiling is unknown. */
236
+ outputHeadroomRatio: number | null
237
+ /** overheadMs / (upstreamMs + overheadMs), 0..1; the share spent in transport. */
238
+ transportOverheadRatio: number | null
239
+ }
240
+
241
+ /** LLM-friendly export of a run's model activity (mirrors `llmMetricsExportSchema`). */
242
+ export interface LlmMetricsExport {
243
+ kind: 'cat-factory.llm-metrics-export'
244
+ version: 1
245
+ executionId: string
246
+ generatedAt: number
247
+ totals: {
248
+ calls: number
249
+ promptTokens: number
250
+ completionTokens: number
251
+ upstreamMs: number
252
+ overheadMs: number
253
+ transportOverheadRatio: number | null
254
+ errors: number
255
+ warnings: number
256
+ truncatedCalls: number
257
+ }
258
+ insights: LlmExportInsight[]
259
+ calls: LlmCallMetric[]
260
+ }
261
+
262
+ /** One agent's slot in a running pipeline. */
263
+ export interface PipelineStep {
264
+ /**
265
+ * Id of the run this step belongs to (always the enclosing ExecutionInstance's id);
266
+ * surfaced on every step so a lone step is self-describing for debugging.
267
+ */
268
+ runId?: string
269
+ agentKind: AgentKind
270
+ state: AgentState
271
+ /** 0..1 progress of this individual step */
272
+ progress: number
273
+ /** LLM observability rollup for this step (token use, headroom, latency split). */
274
+ metrics?: StepMetrics | null
275
+ /** live "N/M done" subtask counts while an async (container) step runs */
276
+ subtasks?: StepSubtasks
277
+ /**
278
+ * True while a container-backed step's per-run container is cold-booting (set at
279
+ * dispatch, cleared once the container is up). Drives the "Spinning up container…"
280
+ * phase indicator before any execution progress is available.
281
+ */
282
+ startingContainer?: boolean
283
+ /** present + unresolved => the step (and block) is blocked */
284
+ decision: Decision | null
285
+ /** whether a human approval gate fires after this step (from the pipeline) */
286
+ requiresApproval?: boolean
287
+ /** the live approval gate for this step; pending => the run is blocked on a human */
288
+ approval?: StepApproval | null
289
+ /**
290
+ * Live companion state when this step is a companion kind: its quality bar, rework
291
+ * budget, and the full sequence of verdicts (one per correction cycle). Absent on
292
+ * non-companion steps.
293
+ */
294
+ companion?: StepCompanion | null
295
+ /**
296
+ * Consensus config for this step, copied from the pipeline at run start; present (with
297
+ * `enabled`) when the step runs through the multi-model consensus mechanism. Absent ⇒
298
+ * standard single-actor agent.
299
+ */
300
+ consensus?: ConsensusStepConfig | null
301
+ /** text the agent produced for this step (when LLM execution is enabled). */
302
+ output?: string
303
+ /** identifier of the model that produced `output`, for transparency. */
304
+ model?: string
305
+ /** prompt-fragment library ids folded into this step (manual ∪ selector pick). */
306
+ selectedFragmentIds?: string[]
307
+ /** epoch ms the step first began executing (transitioned to `working`). */
308
+ startedAt?: number | null
309
+ /** epoch ms the step finished (transitioned to `done`); with `startedAt` gives duration. */
310
+ finishedAt?: number | null
311
+ /**
312
+ * epoch ms the step parked on a human (approval / decision / iteration-cap gate),
313
+ * freezing its duration while it waits for input; cleared (null) once it resumes or
314
+ * finishes. The counterpart of `finishedAt` for the "waiting on input" freeze.
315
+ */
316
+ pausedAt?: number | null
317
+ /**
318
+ * Live Tester→Fixer loop state when this step is a `tester` kind: which phase is in
319
+ * flight (testing vs fixing the issues it found), the fixer attempt budget, and the
320
+ * latest structured test report. Absent on non-tester steps.
321
+ */
322
+ test?: TesterStepState | null
323
+ /**
324
+ * Live gate state when this step is a polling gate (`ci` / `conflicts`): which phase
325
+ * is in flight (checking the precheck vs a helper working), the helper attempt
326
+ * budget, the gated commit, and the latest precheck verdict + failure detail. Absent
327
+ * on non-gate steps. Mirrors `gateStepStateSchema`.
328
+ */
329
+ gate?: GateStepState | null
330
+ }
331
+
332
+ /** One failing CI check the gate's precheck saw (mirrors `gateFailingCheckSchema`). */
333
+ export interface GateFailingCheck {
334
+ name: string
335
+ conclusion: string | null
336
+ }
337
+
338
+ /** Live state of a polling gate step (`ci` / `conflicts`); mirrors `gateStepStateSchema`. */
339
+ export interface GateStepState {
340
+ phase: 'checking' | 'working'
341
+ /** how many helper-agent attempts have been dispatched so far */
342
+ attempts: number
343
+ /** ceiling on helper attempts (from the task's merge preset) */
344
+ maxAttempts: number
345
+ /** the PR head commit being gated, once resolved */
346
+ headSha?: string | null
347
+ /** the most recent precheck verdict (why the gate is looping vs idle-passing) */
348
+ lastVerdict?: 'pass' | 'pending' | 'fail' | null
349
+ /** human-readable summary of the latest failing precheck (failing checks / conflict reason) */
350
+ lastFailureSummary?: string | null
351
+ /** structured failing checks behind the summary (CI gate only; absent for conflicts) */
352
+ failingChecks?: GateFailingCheck[] | null
353
+ }
354
+
355
+ /** Live state of a `tester` step's Tester→Fixer loop (mirrors `testerStepStateSchema`). */
356
+ export interface TesterStepState {
357
+ phase: 'testing' | 'fixing'
358
+ /** how many fixer attempts have been dispatched so far */
359
+ attempts: number
360
+ /** ceiling on fixer attempts (from the task's merge preset) */
361
+ maxAttempts: number
362
+ /** the most recent Tester report (what was tested, outcomes, concerns, greenlight) */
363
+ lastReport?: TestReport | null
364
+ }
365
+
366
+ /** A pipeline instance running against one block. */
367
+ export interface ExecutionInstance {
368
+ id: string
369
+ blockId: string
370
+ pipelineId: string
371
+ pipelineName: string
372
+ steps: PipelineStep[]
373
+ /** index into steps of the currently active step */
374
+ currentStep: number
375
+ /**
376
+ * 'paused' = halted by the spend safeguard until the budget frees up;
377
+ * 'failed' = the run faulted (see `failure`) — surfaces the shared failure
378
+ * banner + retry, instead of the old success-looking `pr_ready` lie.
379
+ */
380
+ status: 'running' | 'blocked' | 'done' | 'paused' | 'failed'
381
+ /** Structured failure diagnostics when `status` is `failed`; null otherwise. */
382
+ failure?: AgentFailure | null
383
+ }
@@ -0,0 +1,72 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Prompt-fragment library (ADR 0006). Mirrors the `@cat-factory/contracts`
3
+ // fragment-library schemas: the managed, tenant-scoped catalog a workspace
4
+ // resolves (built-in ∪ account ∪ workspace), plus the repo sources that feed it.
5
+ // ---------------------------------------------------------------------------
6
+
7
+ import type { AgentKind, BlockType } from './domain'
8
+ import type { PromptFragment } from './models'
9
+
10
+ /** Which scope owns a managed fragment / source. */
11
+ export type FragmentOwnerKind = 'account' | 'workspace'
12
+
13
+ /** The tier a resolved fragment originates from after override-by-id. */
14
+ export type FragmentTier = 'builtin' | 'account' | 'workspace'
15
+
16
+ /** Inputs for creating a hand-authored fragment at a tier. */
17
+ export interface CreatePromptFragmentInput {
18
+ id?: string
19
+ title: string
20
+ category?: string
21
+ summary: string
22
+ body: string
23
+ tags?: string[]
24
+ appliesTo?: { blockTypes?: BlockType[]; agentKinds?: AgentKind[] }
25
+ version?: string
26
+ }
27
+
28
+ /** Partial patch for editing a fragment at a tier. */
29
+ export type UpdatePromptFragmentInput = Partial<CreatePromptFragmentInput>
30
+
31
+ /** A fragment after the three tiers are merged for a workspace. */
32
+ export interface ResolvedFragment extends PromptFragment {
33
+ tier: FragmentTier
34
+ }
35
+
36
+ /** A repo directory linked as a source of Markdown guideline files. */
37
+ export interface FragmentSource {
38
+ id: string
39
+ ownerKind: FragmentOwnerKind
40
+ ownerId: string
41
+ repoOwner: string
42
+ repoName: string
43
+ gitRef: string
44
+ dirPath: string
45
+ lastSyncedSha: string | null
46
+ lastSyncedAt: number | null
47
+ createdAt: number
48
+ }
49
+
50
+ /** Inputs for linking a repo directory as a fragment source. */
51
+ export interface LinkFragmentSourceInput {
52
+ repoOwner: string
53
+ repoName: string
54
+ gitRef?: string
55
+ dirPath?: string
56
+ }
57
+
58
+ /** Outcome of resyncing a source. */
59
+ export interface FragmentSyncResult {
60
+ upserted: number
61
+ tombstoned: number
62
+ unchanged: number
63
+ lastSyncedSha: string | null
64
+ }
65
+
66
+ /** Cheap "check for changes" result (no writes); powers the resync badge. */
67
+ export interface FragmentSourceStatus {
68
+ changed: boolean
69
+ changedCount: number
70
+ lastSyncedSha: string | null
71
+ remoteSha: string | null
72
+ }
@@ -0,0 +1,173 @@
1
+ // ---------------------------------------------------------------------------
2
+ // GitHub integration. The backend's `GitHubModule` projects GitHub data
3
+ // (repos/branches, pull requests/issues) into D1 and serves it fast and
4
+ // rate-limit-free, alongside connect/resync/write endpoints. These mirror the
5
+ // `@cat-factory/contracts` GitHub schemas so responses drop straight into the
6
+ // Pinia store, just as the document-source types do for Confluence/Notion.
7
+ // ---------------------------------------------------------------------------
8
+
9
+ /** A workspace's GitHub App installation, as exposed to clients (no token). */
10
+ export interface GitHubConnection {
11
+ installationId: number
12
+ accountLogin: string
13
+ targetType: 'Organization' | 'User'
14
+ connectedAt: number
15
+ /**
16
+ * Whether cat-factory can create repos under this account itself (privileged
17
+ * App tier). When true, the bootstrap UI drops the manual "create on GitHub"
18
+ * step. Defaults to false for older backends that don't send it.
19
+ */
20
+ canCreateRepos?: boolean
21
+ /**
22
+ * Whether the installation granted the App `workflows: write`. When false, agent
23
+ * pushes that add/update `.github/workflows/*` are rejected by GitHub, so the UI
24
+ * warns the user to grant the permission. Defaults to false for older backends.
25
+ */
26
+ canManageWorkflows?: boolean
27
+ }
28
+
29
+ /**
30
+ * A discoverable App installation for the connect picker. `connected` says
31
+ * whether it's already bound: to THIS workspace, to ANOTHER (so connecting would
32
+ * be rejected), or to NONE (free to connect).
33
+ */
34
+ export interface GitHubInstallationOption {
35
+ installationId: number
36
+ accountLogin: string
37
+ targetType: 'Organization' | 'User'
38
+ accountAvatarUrl: string | null
39
+ connected: 'this' | 'other' | 'none'
40
+ }
41
+
42
+ /** A repository the integration tracks for a workspace. */
43
+ export interface GitHubRepo {
44
+ githubId: number
45
+ installationId: number
46
+ owner: string
47
+ name: string
48
+ defaultBranch: string | null
49
+ private: boolean
50
+ /** Optional link to a board block this repo backs. */
51
+ blockId: string | null
52
+ /** Whether this repo is a monorepo hosting several services (each pinned to a subdirectory). */
53
+ isMonorepo?: boolean
54
+ syncedAt: number
55
+ }
56
+
57
+ /** One directory entry of a repo's tree, used by the monorepo service picker. */
58
+ export interface RepoTreeEntry {
59
+ /** Path relative to the repo root, e.g. `packages/api`. */
60
+ path: string
61
+ /** Base name, e.g. `api`. */
62
+ name: string
63
+ /** `file` | `dir` | `symlink` | `submodule`. */
64
+ type: string
65
+ }
66
+
67
+ export interface GitHubBranch {
68
+ repoGithubId: number
69
+ name: string
70
+ headSha: string
71
+ protected: boolean
72
+ syncedAt: number
73
+ }
74
+
75
+ /**
76
+ * A repo the connected installation can access, annotated with whether the
77
+ * current workspace links it. Drives the per-workspace repo picker — repos are
78
+ * linked explicitly per board, since the installation is shared across an
79
+ * account's workspaces.
80
+ */
81
+ export interface GitHubAvailableRepo {
82
+ githubId: number
83
+ owner: string
84
+ name: string
85
+ defaultBranch: string | null
86
+ private: boolean
87
+ linked: boolean
88
+ /** Whether the (linked) repo is flagged as a monorepo. */
89
+ isMonorepo?: boolean
90
+ }
91
+
92
+ export type GitHubPullRequestState = 'open' | 'closed'
93
+
94
+ export interface GitHubPullRequest {
95
+ repoGithubId: number
96
+ number: number
97
+ githubId: number
98
+ title: string
99
+ state: GitHubPullRequestState
100
+ headRef: string | null
101
+ baseRef: string | null
102
+ headSha: string | null
103
+ merged: boolean
104
+ author: string | null
105
+ updatedAt: number | null
106
+ syncedAt: number
107
+ }
108
+
109
+ export type GitHubIssueState = 'open' | 'closed'
110
+
111
+ export interface GitHubIssue {
112
+ repoGithubId: number
113
+ number: number
114
+ githubId: number
115
+ title: string
116
+ state: GitHubIssueState
117
+ author: string | null
118
+ labels: string[]
119
+ updatedAt: number | null
120
+ syncedAt: number
121
+ }
122
+
123
+ // ---- request inputs -------------------------------------------------------
124
+
125
+ /** Trigger a resync. Defaults to an incremental resync of all tracked repos. */
126
+ export interface ResyncRequest {
127
+ /** Limit the resync to a single repo (by its GitHub numeric id). */
128
+ repoGithubId?: number
129
+ /** Run a full backfill (durable Workflow) instead of an incremental pass. */
130
+ full?: boolean
131
+ }
132
+
133
+ export interface CreateBranchInput {
134
+ name: string
135
+ fromSha: string
136
+ }
137
+
138
+ /** Body for programmatic repo creation (privileged App tier). */
139
+ export interface CreateRepoRequest {
140
+ /** A single GitHub name segment — no "owner/" prefix. */
141
+ name: string
142
+ private?: boolean
143
+ description?: string
144
+ }
145
+
146
+ /** The freshly-created repository returned by the create-repo endpoint. */
147
+ export interface CreatedRepo {
148
+ githubId: number
149
+ owner: string
150
+ name: string
151
+ defaultBranch: string | null
152
+ private: boolean
153
+ }
154
+
155
+ export interface CommitFilesInput {
156
+ branch: string
157
+ message: string
158
+ files: { path: string; content: string }[]
159
+ /** Parent commit to build on; defaults to the branch tip. */
160
+ baseSha?: string
161
+ }
162
+
163
+ export interface OpenPullRequestInput {
164
+ title: string
165
+ head: string
166
+ base: string
167
+ body?: string
168
+ draft?: boolean
169
+ }
170
+
171
+ export interface MergePullRequestInput {
172
+ method?: 'merge' | 'squash' | 'rebase'
173
+ }
@@ -0,0 +1,73 @@
1
+ // ---------------------------------------------------------------------------
2
+ // Per-user "local model runners" — a developer running cat-factory in local (or
3
+ // self-hosted Node) mode can point agents at an LLM running on their OWN machine
4
+ // (Ollama, LM Studio, llama.cpp, vLLM, or any OpenAI-compatible server). A runner
5
+ // is just a provider id + a base URL, configured PER USER (a runner lives on a
6
+ // person's machine) and surfaced automatically in the per-workspace model picker.
7
+ //
8
+ // Mirrors the `@cat-factory/contracts` `localModels` schemas exactly, so a payload
9
+ // returned by the backend drops straight into the Pinia store without translation.
10
+ // ---------------------------------------------------------------------------
11
+
12
+ /** The supported local runner types. The runner type IS the `ModelRef.provider`. */
13
+ export type LocalRunner = 'ollama' | 'lmstudio' | 'llamacpp' | 'vllm' | 'custom'
14
+
15
+ /** Default base URL per runner, for UI prefill. `custom` has none (user supplies it). */
16
+ export const LOCAL_RUNNER_DEFAULTS: Record<LocalRunner, string | null> = {
17
+ ollama: 'http://localhost:11434/v1',
18
+ lmstudio: 'http://localhost:1234/v1',
19
+ llamacpp: 'http://localhost:8080/v1',
20
+ vllm: 'http://localhost:8000/v1',
21
+ custom: null,
22
+ }
23
+
24
+ /** Short display label per runner, shown in the picker as the provider label. */
25
+ export const LOCAL_RUNNER_LABELS: Record<LocalRunner, string> = {
26
+ ollama: 'Ollama',
27
+ lmstudio: 'LM Studio',
28
+ llamacpp: 'llama.cpp',
29
+ vllm: 'vLLM',
30
+ custom: 'Custom',
31
+ }
32
+
33
+ /**
34
+ * A user's configured local runner endpoint, as returned to the SPA. The API key is
35
+ * write-only (never returned); `hasApiKey` reports whether one is stored.
36
+ */
37
+ export interface LocalModelEndpoint {
38
+ provider: LocalRunner
39
+ label: string
40
+ baseUrl: string
41
+ /** Whether a (write-only) API key is stored for this endpoint. */
42
+ hasApiKey: boolean
43
+ /** The model ids the user has enabled from this runner (surfaced in the picker). */
44
+ models: string[]
45
+ createdAt: number
46
+ updatedAt: number
47
+ }
48
+
49
+ /** Create or replace the signed-in user's endpoint for a runner (one per runner). */
50
+ export interface UpsertLocalModelEndpointInput {
51
+ provider: LocalRunner
52
+ label?: string
53
+ baseUrl: string
54
+ /** Optional bearer key (most local runners ignore it); stored encrypted at rest. */
55
+ apiKey?: string
56
+ models: string[]
57
+ }
58
+
59
+ /** Probe a runner endpoint for reachability + the models it currently serves. */
60
+ export interface TestLocalModelEndpointInput {
61
+ provider: LocalRunner
62
+ baseUrl: string
63
+ apiKey?: string
64
+ }
65
+
66
+ /** The result of probing a runner endpoint's `/models`. */
67
+ export interface LocalModelEndpointTestResult {
68
+ reachable: boolean
69
+ /** Model ids the runner reports (empty when unreachable). */
70
+ models: string[]
71
+ /** Human-readable failure reason when `reachable` is false. */
72
+ error?: string
73
+ }