@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.
- package/LICENSE +21 -0
- package/README.md +88 -0
- package/app/app.config.ts +8 -0
- package/app/app.vue +11 -0
- package/app/assets/css/main.css +100 -0
- package/app/components/auth/AuthGate.vue +24 -0
- package/app/components/auth/LoginScreen.vue +143 -0
- package/app/components/auth/UserMenu.vue +39 -0
- package/app/components/board/AddTaskModal.vue +444 -0
- package/app/components/board/AgentFailureCard.vue +97 -0
- package/app/components/board/AgentStopButton.vue +61 -0
- package/app/components/board/BoardCanvas.vue +183 -0
- package/app/components/board/ContextPicker.vue +367 -0
- package/app/components/board/RecurringPipelineModal.vue +219 -0
- package/app/components/board/TaskDependencyEdges.vue +132 -0
- package/app/components/board/nodes/AgentChip.vue +59 -0
- package/app/components/board/nodes/BlockNode.vue +433 -0
- package/app/components/board/nodes/DecisionBadge.vue +27 -0
- package/app/components/board/nodes/DraggableTask.vue +48 -0
- package/app/components/board/nodes/ModuleFrame.vue +97 -0
- package/app/components/board/nodes/TaskCard.vue +359 -0
- package/app/components/board/nodes/TaskPipelineMini.vue +159 -0
- package/app/components/bootstrap/BootstrapModal.vue +665 -0
- package/app/components/clarity/ClarityReviewWindow.vue +611 -0
- package/app/components/consensus/ConsensusSessionWindow.vue +210 -0
- package/app/components/documents/DocumentImportModal.vue +161 -0
- package/app/components/documents/DocumentSourceConnectModal.vue +127 -0
- package/app/components/documents/SpawnPreviewModal.vue +161 -0
- package/app/components/documents/TaskContextDocs.vue +83 -0
- package/app/components/focus/BlockFocusView.vue +171 -0
- package/app/components/fragments/FragmentLibraryPanel.vue +340 -0
- package/app/components/gates/GateResultView.vue +282 -0
- package/app/components/github/AddServiceFromRepoModal.vue +354 -0
- package/app/components/github/GitHubConnect.vue +183 -0
- package/app/components/github/GitHubOnboarding.vue +45 -0
- package/app/components/github/GitHubPanel.vue +584 -0
- package/app/components/github/RepoTreeBrowser.vue +171 -0
- package/app/components/layout/AccountTeamSettings.vue +237 -0
- package/app/components/layout/BoardSwitcher.vue +280 -0
- package/app/components/layout/BoardToolbar.vue +156 -0
- package/app/components/layout/CommandBar.vue +336 -0
- package/app/components/layout/GitHubPatBanner.vue +73 -0
- package/app/components/layout/NotificationsInbox.vue +175 -0
- package/app/components/layout/SideBar.vue +314 -0
- package/app/components/layout/SpendWarningBanner.vue +107 -0
- package/app/components/observability/StepMetricsBar.vue +102 -0
- package/app/components/palettes/AgentPalette.vue +86 -0
- package/app/components/panels/AgentStepDetail.vue +737 -0
- package/app/components/panels/DecisionModal.vue +71 -0
- package/app/components/panels/InspectorPanel.vue +465 -0
- package/app/components/panels/ObservabilityPanel.vue +351 -0
- package/app/components/panels/StepMetadataCard.vue +253 -0
- package/app/components/panels/StepRestartControl.vue +90 -0
- package/app/components/panels/StepResultViewHost.vue +40 -0
- package/app/components/panels/StepTestReport.vue +84 -0
- package/app/components/panels/inspector/ContainerSummary.vue +74 -0
- package/app/components/panels/inspector/RecurringScheduleSettings.vue +178 -0
- package/app/components/panels/inspector/ServiceFragments.vue +82 -0
- package/app/components/panels/inspector/ServiceTestConfig.vue +198 -0
- package/app/components/panels/inspector/TaskAgentConfig.vue +81 -0
- package/app/components/panels/inspector/TaskDependencies.vue +70 -0
- package/app/components/panels/inspector/TaskEstimateBadge.vue +56 -0
- package/app/components/panels/inspector/TaskExecution.vue +364 -0
- package/app/components/panels/inspector/TaskRunSettings.vue +187 -0
- package/app/components/panels/inspector/TaskStructure.vue +96 -0
- package/app/components/pipeline/AgentKindIcon.vue +30 -0
- package/app/components/pipeline/IterationCapPrompt.vue +70 -0
- package/app/components/pipeline/PipelineBuilder.vue +817 -0
- package/app/components/pipeline/PipelineProgress.vue +484 -0
- package/app/components/providers/ApiKeysSection.vue +273 -0
- package/app/components/providers/PersonalCredentialModal.vue +128 -0
- package/app/components/providers/PersonalSubscriptionSection.vue +225 -0
- package/app/components/providers/VendorCredentialsModal.vue +197 -0
- package/app/components/recurring/RecurrenceEditor.vue +124 -0
- package/app/components/requirements/RequirementsReviewWindow.vue +620 -0
- package/app/components/settings/DatadogPanel.vue +213 -0
- package/app/components/settings/LocalModelEndpointsPanel.vue +286 -0
- package/app/components/settings/MergeThresholdsPanel.vue +378 -0
- package/app/components/settings/ModelDefaultsPanel.vue +250 -0
- package/app/components/settings/ServiceFragmentDefaultsPanel.vue +124 -0
- package/app/components/settings/WorkspaceSettingsPanel.vue +142 -0
- package/app/components/slack/SlackPanel.vue +299 -0
- package/app/components/tasks/TaskContextIssues.vue +88 -0
- package/app/components/tasks/TaskImportModal.vue +207 -0
- package/app/components/tasks/TaskSourceConnectModal.vue +133 -0
- package/app/components/testing/TestReportWindow.vue +404 -0
- package/app/composables/api/accounts.ts +81 -0
- package/app/composables/api/auth.ts +45 -0
- package/app/composables/api/board.ts +101 -0
- package/app/composables/api/bootstrap.ts +62 -0
- package/app/composables/api/context.ts +25 -0
- package/app/composables/api/documents.ts +74 -0
- package/app/composables/api/execution.ts +127 -0
- package/app/composables/api/fragments.ts +71 -0
- package/app/composables/api/github.ts +131 -0
- package/app/composables/api/models.ts +127 -0
- package/app/composables/api/notifications.ts +23 -0
- package/app/composables/api/presets.ts +29 -0
- package/app/composables/api/recurring.ts +68 -0
- package/app/composables/api/releaseHealth.ts +43 -0
- package/app/composables/api/reviews.ts +146 -0
- package/app/composables/api/slack.ts +54 -0
- package/app/composables/api/tasks.ts +72 -0
- package/app/composables/api/workspaces.ts +36 -0
- package/app/composables/useApi.ts +89 -0
- package/app/composables/useBlockDrag.ts +90 -0
- package/app/composables/useBlockQueries.ts +154 -0
- package/app/composables/useBoardFlow.ts +11 -0
- package/app/composables/useContextLinking.ts +65 -0
- package/app/composables/useDepLabels.ts +26 -0
- package/app/composables/useFrameResize.ts +54 -0
- package/app/composables/useResultView.ts +48 -0
- package/app/composables/useReviewStage.ts +40 -0
- package/app/composables/useSemanticZoom.ts +31 -0
- package/app/composables/useStepApproval.ts +233 -0
- package/app/composables/useStepProse.ts +78 -0
- package/app/composables/useStepTimer.ts +63 -0
- package/app/composables/useTaskExpansion.ts +92 -0
- package/app/composables/useWorkspaceStream.ts +155 -0
- package/app/docs/architecture.md +31 -0
- package/app/pages/index.vue +141 -0
- package/app/stores/accounts.ts +152 -0
- package/app/stores/agentConfig.ts +35 -0
- package/app/stores/agentRuns.ts +122 -0
- package/app/stores/agents.ts +40 -0
- package/app/stores/apiKeys.ts +108 -0
- package/app/stores/auth.ts +166 -0
- package/app/stores/board.spec.ts +205 -0
- package/app/stores/board.ts +286 -0
- package/app/stores/bootstrap.ts +97 -0
- package/app/stores/clarity.ts +196 -0
- package/app/stores/consensus.ts +60 -0
- package/app/stores/documents.ts +176 -0
- package/app/stores/execution.ts +273 -0
- package/app/stores/fragmentLibrary.ts +147 -0
- package/app/stores/fragments.ts +40 -0
- package/app/stores/github.ts +305 -0
- package/app/stores/localModels.ts +51 -0
- package/app/stores/mergePresets.ts +58 -0
- package/app/stores/modelDefaults.ts +76 -0
- package/app/stores/models.ts +134 -0
- package/app/stores/notifications.ts +70 -0
- package/app/stores/observability.ts +144 -0
- package/app/stores/personalSubscriptions.ts +215 -0
- package/app/stores/pipelines.ts +327 -0
- package/app/stores/recurringPipelines.ts +112 -0
- package/app/stores/releaseHealth.ts +75 -0
- package/app/stores/requirements.spec.ts +94 -0
- package/app/stores/requirements.ts +208 -0
- package/app/stores/serviceFragmentDefaults.ts +29 -0
- package/app/stores/services.ts +87 -0
- package/app/stores/slack.ts +142 -0
- package/app/stores/taskExpansion.ts +36 -0
- package/app/stores/tasks.spec.ts +71 -0
- package/app/stores/tasks.ts +176 -0
- package/app/stores/tracker.ts +27 -0
- package/app/stores/ui.ts +434 -0
- package/app/stores/vendorCredentials.ts +54 -0
- package/app/stores/workspace.ts +215 -0
- package/app/stores/workspaceSettings.ts +36 -0
- package/app/types/accounts.ts +77 -0
- package/app/types/bootstrap.ts +83 -0
- package/app/types/clarity.ts +59 -0
- package/app/types/consensus.ts +91 -0
- package/app/types/documents.ts +104 -0
- package/app/types/domain.ts +495 -0
- package/app/types/execution.ts +383 -0
- package/app/types/fragments.ts +72 -0
- package/app/types/github.ts +173 -0
- package/app/types/localModels.ts +73 -0
- package/app/types/merge.ts +71 -0
- package/app/types/models.ts +157 -0
- package/app/types/notifications.ts +74 -0
- package/app/types/recurring.ts +69 -0
- package/app/types/releaseHealth.ts +31 -0
- package/app/types/requirements.ts +61 -0
- package/app/types/services.ts +27 -0
- package/app/types/slack.ts +57 -0
- package/app/types/tasks.ts +82 -0
- package/app/types/tracker.ts +18 -0
- package/app/utils/agentOutput.spec.ts +128 -0
- package/app/utils/agentOutput.ts +173 -0
- package/app/utils/catalog.spec.ts +112 -0
- package/app/utils/catalog.ts +455 -0
- package/app/utils/dnd.ts +29 -0
- package/app/utils/observability.ts +52 -0
- package/app/utils/pipelineRender.ts +151 -0
- package/nuxt.config.ts +55 -0
- 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
|
+
}
|