@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,71 @@
|
|
|
1
|
+
// Merge-policy shapes, mirroring `@cat-factory/contracts` (merge.ts). A `merger`
|
|
2
|
+
// agent scores a PR on three 0..1 axes and the engine compares them against the
|
|
3
|
+
// task's resolved threshold preset to auto-merge or raise a review notification.
|
|
4
|
+
|
|
5
|
+
/** A `merger` agent's assessment of a pull request (each axis 0..1). */
|
|
6
|
+
export interface MergeAssessment {
|
|
7
|
+
complexity: number
|
|
8
|
+
risk: number
|
|
9
|
+
impact: number
|
|
10
|
+
rationale: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The highest reviewer-finding severity a task tolerates before the requirements review
|
|
15
|
+
* stops for a human. `none` tolerates nothing; `high` tolerates everything. Ordered
|
|
16
|
+
* none < low < medium < high.
|
|
17
|
+
*/
|
|
18
|
+
export type RequirementConcernLevel = 'none' | 'low' | 'medium' | 'high'
|
|
19
|
+
|
|
20
|
+
/** A named, per-workspace merge policy a task can select. */
|
|
21
|
+
export interface MergeThresholdPreset {
|
|
22
|
+
id: string
|
|
23
|
+
name: string
|
|
24
|
+
/** Auto-merge only when the assessment's complexity is ≤ this. */
|
|
25
|
+
maxComplexity: number
|
|
26
|
+
/** Auto-merge only when the assessment's risk is ≤ this. */
|
|
27
|
+
maxRisk: number
|
|
28
|
+
/** Auto-merge only when the assessment's impact is ≤ this. */
|
|
29
|
+
maxImpact: number
|
|
30
|
+
/** How many times the CI-fixer may try before the CI gate gives up. */
|
|
31
|
+
ciMaxAttempts: number
|
|
32
|
+
/** How many reviewer passes the requirements-review loop runs before asking the human. */
|
|
33
|
+
maxRequirementIterations: number
|
|
34
|
+
/** Findings at or below this severity auto-pass without stopping for a human. */
|
|
35
|
+
maxRequirementConcernAllowed: RequirementConcernLevel
|
|
36
|
+
/** Minutes the post-release-health gate watches the release's monitors/SLOs after deploy. */
|
|
37
|
+
releaseWatchWindowMinutes: number
|
|
38
|
+
/** How many on-call investigations the post-release-health gate may dispatch. */
|
|
39
|
+
releaseMaxAttempts: number
|
|
40
|
+
/** The workspace's fallback preset, used by tasks that pick none. */
|
|
41
|
+
isDefault: boolean
|
|
42
|
+
createdAt: number
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Create a merge threshold preset. */
|
|
46
|
+
export interface CreateMergePresetInput {
|
|
47
|
+
name: string
|
|
48
|
+
maxComplexity: number
|
|
49
|
+
maxRisk: number
|
|
50
|
+
maxImpact: number
|
|
51
|
+
ciMaxAttempts: number
|
|
52
|
+
maxRequirementIterations: number
|
|
53
|
+
maxRequirementConcernAllowed: RequirementConcernLevel
|
|
54
|
+
releaseWatchWindowMinutes?: number
|
|
55
|
+
releaseMaxAttempts?: number
|
|
56
|
+
isDefault?: boolean
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Patch a merge threshold preset (all fields optional). */
|
|
60
|
+
export interface UpdateMergePresetInput {
|
|
61
|
+
name?: string
|
|
62
|
+
maxComplexity?: number
|
|
63
|
+
maxRisk?: number
|
|
64
|
+
maxImpact?: number
|
|
65
|
+
ciMaxAttempts?: number
|
|
66
|
+
maxRequirementIterations?: number
|
|
67
|
+
maxRequirementConcernAllowed?: RequirementConcernLevel
|
|
68
|
+
releaseWatchWindowMinutes?: number
|
|
69
|
+
releaseMaxAttempts?: number
|
|
70
|
+
isDefault?: boolean
|
|
71
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Model selection & best-practice prompt fragments. Mirrors the
|
|
3
|
+
// `@cat-factory/contracts` schemas served read-only by the backend.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
import type { AgentKind, BlockType } from './domain'
|
|
7
|
+
|
|
8
|
+
/** Subscription vendors whose pooled tokens drive the Claude Code / Codex harnesses. */
|
|
9
|
+
export type SubscriptionVendor = 'claude' | 'codex' | 'glm' | 'kimi' | 'deepseek'
|
|
10
|
+
|
|
11
|
+
/** Informational list price (per 1M tokens) for a model flavour. */
|
|
12
|
+
export interface ModelCost {
|
|
13
|
+
inputPerMillion: number
|
|
14
|
+
outputPerMillion: number
|
|
15
|
+
currency: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A selectable LLM model, resolved to the flavour in use for this deployment
|
|
20
|
+
* (served by `GET /models`). Mirrors `ModelOption` in `@cat-factory/contracts`.
|
|
21
|
+
* The base `flavor`/`provider`/`model` is the always-available fallback
|
|
22
|
+
* (cloudflare/direct), or the subscription itself for subscription-only models.
|
|
23
|
+
* `subscription` (when present) is the alternative the picker prefers once the
|
|
24
|
+
* workspace has a token for its vendor.
|
|
25
|
+
*/
|
|
26
|
+
export interface ModelOption {
|
|
27
|
+
id: string
|
|
28
|
+
label: string
|
|
29
|
+
description: string
|
|
30
|
+
flavor: 'cloudflare' | 'direct' | 'subscription'
|
|
31
|
+
/**
|
|
32
|
+
* Whether this model is actually selectable for the workspace: a direct API key for
|
|
33
|
+
* its provider, a connected subscription vendor, or Cloudflare AI enabled. Absent on
|
|
34
|
+
* the deployment-level catalog; present on the per-workspace `/workspaces/:id/models`.
|
|
35
|
+
*/
|
|
36
|
+
available?: boolean
|
|
37
|
+
providerLabel: string
|
|
38
|
+
provider: string
|
|
39
|
+
model: string
|
|
40
|
+
vendor?: SubscriptionVendor
|
|
41
|
+
cost?: ModelCost
|
|
42
|
+
contextTokens?: number
|
|
43
|
+
/** True when the effective flavour is flat-rate quota (not budget-metered). */
|
|
44
|
+
quotaBased?: boolean
|
|
45
|
+
/** The alternative subscription flavour for a dual-mode model (GLM/Kimi). */
|
|
46
|
+
subscription?: {
|
|
47
|
+
vendor: SubscriptionVendor
|
|
48
|
+
providerLabel: string
|
|
49
|
+
provider: string
|
|
50
|
+
model: string
|
|
51
|
+
cost?: ModelCost
|
|
52
|
+
contextTokens?: number
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Status of a user's personal (individual-usage) subscription — Claude consumer
|
|
58
|
+
* subscriptions are licensed per individual, so they're stored per-user (not pooled)
|
|
59
|
+
* and unlocked with a personal password. Metadata only; never the token.
|
|
60
|
+
*/
|
|
61
|
+
export interface PersonalSubscriptionStatus {
|
|
62
|
+
vendor: SubscriptionVendor
|
|
63
|
+
label: string
|
|
64
|
+
createdAt: number
|
|
65
|
+
updatedAt: number
|
|
66
|
+
lastUsedAt: number | null
|
|
67
|
+
/** Subscription's own expiry (null = no fixed end date). */
|
|
68
|
+
expiresAt: number | null
|
|
69
|
+
/** Whole days until `expiresAt` (negative once lapsed; null when no expiry). */
|
|
70
|
+
expiresInDays: number | null
|
|
71
|
+
expired: boolean
|
|
72
|
+
/** Whether renewal should be surfaced now (expiry within the warning window). */
|
|
73
|
+
renewSoon: boolean
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Connect (or replace) the signed-in user's personal subscription for a vendor. */
|
|
77
|
+
export interface StorePersonalSubscriptionInput {
|
|
78
|
+
vendor: SubscriptionVendor
|
|
79
|
+
label: string
|
|
80
|
+
token: string
|
|
81
|
+
/** Personal password gating the second encryption layer; never stored. */
|
|
82
|
+
password: string
|
|
83
|
+
/** Epoch ms the subscription expires (for renewal warnings); optional. */
|
|
84
|
+
expiresAt?: number | null
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** The scope a stored direct-provider API key belongs to. */
|
|
88
|
+
export type ApiKeyScope = 'account' | 'workspace' | 'user'
|
|
89
|
+
|
|
90
|
+
/** The direct providers that own a poolable API key. */
|
|
91
|
+
export type ApiKeyProvider =
|
|
92
|
+
| 'openai'
|
|
93
|
+
| 'anthropic'
|
|
94
|
+
| 'qwen'
|
|
95
|
+
| 'deepseek'
|
|
96
|
+
| 'moonshot'
|
|
97
|
+
| 'openrouter'
|
|
98
|
+
| 'litellm'
|
|
99
|
+
|
|
100
|
+
/** A connected direct-provider API key (metadata + usage), never the secret. */
|
|
101
|
+
export interface ApiKey {
|
|
102
|
+
id: string
|
|
103
|
+
scope: ApiKeyScope
|
|
104
|
+
scopeId: string
|
|
105
|
+
provider: ApiKeyProvider
|
|
106
|
+
label: string
|
|
107
|
+
createdAt: number
|
|
108
|
+
lastUsedAt: number | null
|
|
109
|
+
inputTokens: number
|
|
110
|
+
outputTokens: number
|
|
111
|
+
requestCount: number
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/** Add a direct-provider API key to a pool. `key` is write-only (the raw secret). */
|
|
115
|
+
export interface AddApiKeyInput {
|
|
116
|
+
provider: ApiKeyProvider
|
|
117
|
+
label: string
|
|
118
|
+
key: string
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** A connected subscription credential (metadata + usage), never the secret. */
|
|
122
|
+
export interface VendorCredential {
|
|
123
|
+
id: string
|
|
124
|
+
vendor: SubscriptionVendor
|
|
125
|
+
label: string
|
|
126
|
+
createdAt: number
|
|
127
|
+
lastUsedAt: number | null
|
|
128
|
+
inputTokens: number
|
|
129
|
+
outputTokens: number
|
|
130
|
+
requestCount: number
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* A curated best-practice "prompt fragment" served read-only by the backend
|
|
135
|
+
* (`GET /prompt-fragments`). Users pick which apply to a block; the backend folds
|
|
136
|
+
* the selected fragments' bodies into the agent system prompt at run time.
|
|
137
|
+
*/
|
|
138
|
+
export interface PromptFragment {
|
|
139
|
+
id: string
|
|
140
|
+
version: string
|
|
141
|
+
title: string
|
|
142
|
+
category: string
|
|
143
|
+
summary: string
|
|
144
|
+
body: string
|
|
145
|
+
appliesTo?: {
|
|
146
|
+
blockTypes?: BlockType[]
|
|
147
|
+
agentKinds?: AgentKind[]
|
|
148
|
+
}
|
|
149
|
+
/** Free-form tags the relevance selector uses (managed/sourced fragments only). */
|
|
150
|
+
tags?: string[]
|
|
151
|
+
/** Provenance when sourced from a repo; absent for built-in/hand-authored. */
|
|
152
|
+
source?: {
|
|
153
|
+
sourceId: string
|
|
154
|
+
path: string
|
|
155
|
+
sha: string
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// Notification shapes, mirroring `@cat-factory/contracts` (notifications.ts). A
|
|
2
|
+
// notification is a first-class, human-actionable item surfaced on the board that
|
|
3
|
+
// outlives the run that raised it (a PR awaiting a merge decision, a completed
|
|
4
|
+
// pipeline awaiting confirmation, CI that gave up).
|
|
5
|
+
|
|
6
|
+
import type { MergeAssessment } from './merge'
|
|
7
|
+
|
|
8
|
+
export type NotificationType =
|
|
9
|
+
| 'merge_review'
|
|
10
|
+
| 'pipeline_complete'
|
|
11
|
+
| 'ci_failed'
|
|
12
|
+
| 'test_failed'
|
|
13
|
+
| 'requirement_review'
|
|
14
|
+
| 'clarity_review'
|
|
15
|
+
| 'release_regression'
|
|
16
|
+
| 'decision_required'
|
|
17
|
+
export type NotificationStatus = 'open' | 'acted' | 'dismissed'
|
|
18
|
+
|
|
19
|
+
/** The on-call agent's recommendation on a `release_regression`. */
|
|
20
|
+
export type OnCallRecommendation = 'revert' | 'hold' | 'monitor'
|
|
21
|
+
|
|
22
|
+
/** The on-call agent's assessment of a post-release regression. */
|
|
23
|
+
export interface OnCallAssessment {
|
|
24
|
+
culpritConfidence: number
|
|
25
|
+
recommendation: OnCallRecommendation
|
|
26
|
+
rationale: string
|
|
27
|
+
evidence?: string[]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A regressed monitor/SLO on a `release_regression`. */
|
|
31
|
+
export interface ReleaseSignal {
|
|
32
|
+
kind: 'monitor' | 'slo'
|
|
33
|
+
id: string
|
|
34
|
+
name: string
|
|
35
|
+
state: 'ok' | 'warn' | 'alert' | 'no_data'
|
|
36
|
+
detail?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Optional structured detail for rendering a notification card. */
|
|
40
|
+
export interface NotificationPayload {
|
|
41
|
+
assessment?: MergeAssessment
|
|
42
|
+
prUrl?: string
|
|
43
|
+
pipelineName?: string
|
|
44
|
+
findingCount?: number
|
|
45
|
+
/** The on-call assessment, on a `release_regression`. */
|
|
46
|
+
onCallAssessment?: OnCallAssessment
|
|
47
|
+
/** The regressed monitors/SLOs, on a `release_regression`. */
|
|
48
|
+
releaseSignals?: ReleaseSignal[]
|
|
49
|
+
/** A proposed revert PR URL, when known. */
|
|
50
|
+
revertUrl?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** A human-actionable item surfaced on the board. */
|
|
54
|
+
/**
|
|
55
|
+
* Render urgency. A notification starts `normal` (the inbox's usual per-type colour) and
|
|
56
|
+
* is escalated to `urgent` (red) by the backend's sweep once it has waited for a human
|
|
57
|
+
* past the workspace's `waitingEscalationMinutes` threshold. Absent ⇒ `normal`.
|
|
58
|
+
*/
|
|
59
|
+
export type NotificationSeverity = 'normal' | 'urgent'
|
|
60
|
+
|
|
61
|
+
export interface Notification {
|
|
62
|
+
id: string
|
|
63
|
+
type: NotificationType
|
|
64
|
+
status: NotificationStatus
|
|
65
|
+
/** Render urgency (yellow vs red); escalated by the backend sweep. Absent ⇒ normal. */
|
|
66
|
+
severity?: NotificationSeverity
|
|
67
|
+
blockId: string | null
|
|
68
|
+
executionId: string | null
|
|
69
|
+
title: string
|
|
70
|
+
body: string
|
|
71
|
+
payload?: NotificationPayload | null
|
|
72
|
+
createdAt: number
|
|
73
|
+
resolvedAt: number | null
|
|
74
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// Recurring-pipeline shapes, mirroring `@cat-factory/contracts` (recurring.ts). A
|
|
2
|
+
// schedule attaches a pipeline to a service frame and re-runs it on a cadence —
|
|
3
|
+
// run every `intervalHours`, constrained to an optional allowed window (weekdays +
|
|
4
|
+
// an hour-of-day range, in the schedule's timezone). Each schedule owns one reused
|
|
5
|
+
// on-board task block; firing it starts the pipeline against that block.
|
|
6
|
+
|
|
7
|
+
/** Template a schedule was created from; drives the seeded block description. */
|
|
8
|
+
export type ScheduleTemplate = 'dep-update' | 'tech-debt' | 'custom'
|
|
9
|
+
|
|
10
|
+
/** How often a schedule fires and when it is allowed to. */
|
|
11
|
+
export interface Recurrence {
|
|
12
|
+
/** Base cadence in hours (≥1). */
|
|
13
|
+
intervalHours: number
|
|
14
|
+
/** Allowed weekdays (0=Sun..6=Sat). Empty = every day. */
|
|
15
|
+
weekdays: number[]
|
|
16
|
+
/** Inclusive start of the allowed hour-of-day window, or null for no lower bound. */
|
|
17
|
+
windowStartHour: number | null
|
|
18
|
+
/** Exclusive end of the allowed hour-of-day window, or null for no upper bound. */
|
|
19
|
+
windowEndHour: number | null
|
|
20
|
+
/** IANA timezone the weekday/hour window is evaluated in (e.g. 'UTC'). */
|
|
21
|
+
timezone: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A recurring pipeline attached to a service. */
|
|
25
|
+
export interface PipelineSchedule {
|
|
26
|
+
id: string
|
|
27
|
+
/** The reused on-board task block the pipeline runs against. */
|
|
28
|
+
blockId: string
|
|
29
|
+
/** The service frame it lives in. */
|
|
30
|
+
frameId: string
|
|
31
|
+
pipelineId: string
|
|
32
|
+
template: ScheduleTemplate
|
|
33
|
+
name: string
|
|
34
|
+
recurrence: Recurrence
|
|
35
|
+
enabled: boolean
|
|
36
|
+
lastRunAt: number | null
|
|
37
|
+
/** Computed epoch-ms of the next eligible fire. */
|
|
38
|
+
nextRunAt: number
|
|
39
|
+
createdAt: number
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** One historical fire of a schedule (retained ~1 week), shown in the inspector. */
|
|
43
|
+
export interface ScheduleRun {
|
|
44
|
+
id: string
|
|
45
|
+
scheduleId: string
|
|
46
|
+
executionId: string | null
|
|
47
|
+
status: 'running' | 'done' | 'failed' | 'skipped'
|
|
48
|
+
startedAt: number
|
|
49
|
+
finishedAt: number | null
|
|
50
|
+
outcome: string | null
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface CreateScheduleInput {
|
|
54
|
+
frameId: string
|
|
55
|
+
pipelineId: string
|
|
56
|
+
template?: ScheduleTemplate
|
|
57
|
+
name: string
|
|
58
|
+
recurrence: Recurrence
|
|
59
|
+
enabled?: boolean
|
|
60
|
+
/** The prompt/description for the reused on-board task; empty → the template seed. */
|
|
61
|
+
description?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface UpdateScheduleInput {
|
|
65
|
+
name?: string
|
|
66
|
+
pipelineId?: string
|
|
67
|
+
recurrence?: Recurrence
|
|
68
|
+
enabled?: boolean
|
|
69
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Datadog post-release-health settings shapes, mirroring `@cat-factory/contracts`
|
|
2
|
+
// (release.ts). Per-workspace Datadog connection (keys write-only, never read back) and
|
|
3
|
+
// the per-block monitor/SLO mappings the post-release-health gate reads.
|
|
4
|
+
|
|
5
|
+
/** What `GET /datadog/connection` returns — never the secret keys. */
|
|
6
|
+
export interface DatadogConnectionView {
|
|
7
|
+
connected: boolean
|
|
8
|
+
site: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Set/replace the workspace's Datadog connection. */
|
|
12
|
+
export interface UpsertDatadogConnectionInput {
|
|
13
|
+
site: string
|
|
14
|
+
apiKey: string
|
|
15
|
+
appKey: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A block's monitor/SLO mapping for the post-release-health gate. */
|
|
19
|
+
export interface ReleaseHealthConfig {
|
|
20
|
+
blockId: string
|
|
21
|
+
monitorIds: string[]
|
|
22
|
+
sloIds: string[]
|
|
23
|
+
envTag: string | null
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** Create/replace a block's release-health config. */
|
|
27
|
+
export interface UpsertReleaseHealthConfigInput {
|
|
28
|
+
monitorIds?: string[]
|
|
29
|
+
sloIds?: string[]
|
|
30
|
+
envTag?: string | null
|
|
31
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Requirements-review wire types. Mirror of `@cat-factory/contracts`'
|
|
2
|
+
// requirements.ts, kept in sync by hand like the rest of `~/types/*` (the SPA
|
|
3
|
+
// does not import the backend package directly).
|
|
4
|
+
//
|
|
5
|
+
// A stateless reviewer agent inspects a block's collected requirements and
|
|
6
|
+
// raises questions / gaps / clarifications; a human answers or dismisses each;
|
|
7
|
+
// then the agent folds the answers back into the block's requirements.
|
|
8
|
+
|
|
9
|
+
export type ReviewItemCategory = 'gap' | 'clarification' | 'assumption' | 'risk' | 'question'
|
|
10
|
+
|
|
11
|
+
export type ReviewItemSeverity = 'low' | 'medium' | 'high'
|
|
12
|
+
|
|
13
|
+
export type ReviewItemStatus = 'open' | 'answered' | 'resolved' | 'dismissed'
|
|
14
|
+
|
|
15
|
+
export interface RequirementReviewItem {
|
|
16
|
+
id: string
|
|
17
|
+
category: ReviewItemCategory
|
|
18
|
+
severity: ReviewItemSeverity
|
|
19
|
+
title: string
|
|
20
|
+
detail: string
|
|
21
|
+
status: ReviewItemStatus
|
|
22
|
+
reply: string | null
|
|
23
|
+
createdAt: number
|
|
24
|
+
updatedAt: number
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* - `ready`: the reviewer raised findings awaiting human answers/dismissals.
|
|
29
|
+
* - `incorporating`: transient; the driver is folding the answers into a document (the FIRST
|
|
30
|
+
* async stage — the user is back on the board).
|
|
31
|
+
* - `reviewing`: transient; the reviewer is RE-reviewing the folded document (the SECOND
|
|
32
|
+
* async stage). Distinct from `incorporating` so the UI can show which stage is running.
|
|
33
|
+
* - `merged`: the companion produced a document (an internal transient on the async path).
|
|
34
|
+
* - `exceeded`: the iteration cap was hit with findings open — awaiting the human's choice.
|
|
35
|
+
* - `incorporated`: terminal; the requirements phase is settled.
|
|
36
|
+
*/
|
|
37
|
+
export type RequirementReviewStatus =
|
|
38
|
+
| 'ready'
|
|
39
|
+
| 'incorporating'
|
|
40
|
+
| 'reviewing'
|
|
41
|
+
| 'merged'
|
|
42
|
+
| 'exceeded'
|
|
43
|
+
| 'incorporated'
|
|
44
|
+
|
|
45
|
+
/** How a human resolves a review that hit its iteration cap. */
|
|
46
|
+
export type ResolveRequirementsExceededChoice = 'extra-round' | 'proceed' | 'stop-reset'
|
|
47
|
+
|
|
48
|
+
export interface RequirementReview {
|
|
49
|
+
id: string
|
|
50
|
+
blockId: string
|
|
51
|
+
status: RequirementReviewStatus
|
|
52
|
+
items: RequirementReviewItem[]
|
|
53
|
+
model: string | null
|
|
54
|
+
incorporatedRequirements: string | null
|
|
55
|
+
/** Reviewer passes run so far (initial review is 1; each re-review adds one). */
|
|
56
|
+
iteration: number
|
|
57
|
+
/** The reviewer-pass budget (from the task's merge preset; an extra round bumps it). */
|
|
58
|
+
maxIterations: number
|
|
59
|
+
createdAt: number
|
|
60
|
+
updatedAt: number
|
|
61
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// In-org shared services. Mirrors the `@cat-factory/contracts` `services` wire schemas:
|
|
2
|
+
// a `Service` is the account-owned unit of work (a service frame + its subtree + repo),
|
|
3
|
+
// shared across the workspaces that *mount* it; a `WorkspaceMount` places a service onto a
|
|
4
|
+
// workspace board with that board's own frame layout override.
|
|
5
|
+
|
|
6
|
+
export interface Service {
|
|
7
|
+
id: string
|
|
8
|
+
accountId: string | null
|
|
9
|
+
frameBlockId: string
|
|
10
|
+
installationId: number | null
|
|
11
|
+
repoGithubId: number | null
|
|
12
|
+
/** Subdirectory within the linked monorepo this service lives in (null = whole repo). */
|
|
13
|
+
directory?: string | null
|
|
14
|
+
createdAt: number
|
|
15
|
+
/** How many boards mount this service. Set only on the org catalog (for the "Shared" badge). */
|
|
16
|
+
mountCount?: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface WorkspaceMount {
|
|
20
|
+
workspaceId: string
|
|
21
|
+
serviceId: string
|
|
22
|
+
/** This board's frame position override. */
|
|
23
|
+
position: { x: number; y: number }
|
|
24
|
+
/** This board's dragged frame size; null/absent = auto-size. */
|
|
25
|
+
size?: { w: number; h: number } | null
|
|
26
|
+
createdAt: number
|
|
27
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Slack integration. Slack is an extra delivery transport for the existing
|
|
3
|
+
// notification mechanism (merge_review / pipeline_complete / ci_failed), tapping
|
|
4
|
+
// the same NotificationChannel seam server-side. These mirror the
|
|
5
|
+
// `@cat-factory/contracts` Slack schemas so responses drop straight into the store.
|
|
6
|
+
//
|
|
7
|
+
// Two scopes: the connection (+ bot token, never sent here) is per-account; the
|
|
8
|
+
// notification routing is per-workspace; the @-mention member map is per-account.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
import type { NotificationType } from './notifications'
|
|
12
|
+
|
|
13
|
+
/** An account's Slack connection, as exposed to clients — safe metadata only. */
|
|
14
|
+
export interface SlackConnection {
|
|
15
|
+
teamId: string
|
|
16
|
+
teamName: string
|
|
17
|
+
teamIconUrl?: string | null
|
|
18
|
+
botUserId?: string | null
|
|
19
|
+
scopes?: string[]
|
|
20
|
+
connectedAt: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Routing for a single notification type: whether it posts, and where. */
|
|
24
|
+
export interface SlackRoute {
|
|
25
|
+
enabled: boolean
|
|
26
|
+
/** A channel id (`C0123…`) or name (`#general`); empty = unrouted. */
|
|
27
|
+
channel: string
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** A workspace's Slack notification routing. */
|
|
31
|
+
export interface SlackNotificationSettings {
|
|
32
|
+
routes: Partial<Record<NotificationType, SlackRoute>>
|
|
33
|
+
mentionsEnabled: boolean
|
|
34
|
+
updatedAt: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** One internal user id → Slack member id mapping entry. */
|
|
38
|
+
export type SlackMemberRole = 'product' | 'engineering'
|
|
39
|
+
|
|
40
|
+
export interface SlackMemberMappingEntry {
|
|
41
|
+
/** Internal user id (`usr_*`) — the same id the member roster shows. */
|
|
42
|
+
userId: string
|
|
43
|
+
slackUserId: string
|
|
44
|
+
/**
|
|
45
|
+
* Notification role: `product` people are @-mentioned on requirement-review
|
|
46
|
+
* findings; everyone else (`engineering`) only when they created the task.
|
|
47
|
+
* Absent means `engineering`.
|
|
48
|
+
*/
|
|
49
|
+
role?: SlackMemberRole
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A Slack channel option for the routing picker. */
|
|
53
|
+
export interface SlackChannel {
|
|
54
|
+
id: string
|
|
55
|
+
name: string
|
|
56
|
+
isPrivate: boolean
|
|
57
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Task-source integration. Individual issues imported from external task
|
|
3
|
+
// trackers (Jira, …) can be attached to a board task as agent context. These
|
|
4
|
+
// mirror the `@cat-factory/contracts` task schemas; the abstraction is
|
|
5
|
+
// source-agnostic, keyed by `source`. Unlike document sources there is no
|
|
6
|
+
// plan/spawn — an issue is linked for context, never expanded into structure.
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
import type { CredentialField } from './documents'
|
|
10
|
+
|
|
11
|
+
/** The external task trackers cat-factory can link to. */
|
|
12
|
+
export type TaskSourceKind = 'jira' | 'github'
|
|
13
|
+
|
|
14
|
+
export type { CredentialField }
|
|
15
|
+
|
|
16
|
+
/** A source's self-description: drives the generic connect + import UI. */
|
|
17
|
+
export interface TaskSourceDescriptor {
|
|
18
|
+
source: TaskSourceKind
|
|
19
|
+
label: string
|
|
20
|
+
/** Lucide icon name for the source. */
|
|
21
|
+
icon: string
|
|
22
|
+
credentialFields: CredentialField[]
|
|
23
|
+
refLabel: string
|
|
24
|
+
refPlaceholder: string
|
|
25
|
+
/** Whether the source supports searching its catalogue by title/content. */
|
|
26
|
+
searchable?: boolean
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A workspace's connection to a task source (never carries credentials). */
|
|
30
|
+
export interface TaskConnection {
|
|
31
|
+
source: TaskSourceKind
|
|
32
|
+
/** Human-friendly label for what we're connected to (site URL). */
|
|
33
|
+
label: string
|
|
34
|
+
/** When the connection was established (epoch ms). */
|
|
35
|
+
connectedAt: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A single comment on an issue, with its body as lightweight Markdown. */
|
|
39
|
+
export interface TaskComment {
|
|
40
|
+
author: string
|
|
41
|
+
createdAt: string
|
|
42
|
+
body: string
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** An issue imported from a source into the workspace, as a structured record. */
|
|
46
|
+
export interface SourceTask {
|
|
47
|
+
source: TaskSourceKind
|
|
48
|
+
/** The source's canonical key for the issue (e.g. `PROJ-123`). */
|
|
49
|
+
externalId: string
|
|
50
|
+
title: string
|
|
51
|
+
url: string
|
|
52
|
+
/** Workflow status name, e.g. `In Progress`. */
|
|
53
|
+
status: string
|
|
54
|
+
/** Issue type name, e.g. `Bug`. */
|
|
55
|
+
type: string
|
|
56
|
+
/** Assignee display name, or null when unassigned. */
|
|
57
|
+
assignee: string | null
|
|
58
|
+
/** Priority name, or null when none. */
|
|
59
|
+
priority: string | null
|
|
60
|
+
labels: string[]
|
|
61
|
+
/** Issue description as lightweight Markdown. */
|
|
62
|
+
description: string
|
|
63
|
+
comments: TaskComment[]
|
|
64
|
+
/** Short plain-text preview of the issue. */
|
|
65
|
+
excerpt: string
|
|
66
|
+
/** The board block this issue is attached to as context, if any. */
|
|
67
|
+
linkedBlockId: string | null
|
|
68
|
+
syncedAt: number
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** A lean hit from searching a tracker's issues (not yet imported). */
|
|
72
|
+
export interface TaskSearchResult {
|
|
73
|
+
source: TaskSourceKind
|
|
74
|
+
/** The source's canonical key for the issue (re-usable as an import ref). */
|
|
75
|
+
externalId: string
|
|
76
|
+
title: string
|
|
77
|
+
url: string
|
|
78
|
+
/** Workflow status name, e.g. `In Progress` (may be empty). */
|
|
79
|
+
status: string
|
|
80
|
+
/** Short plain-text preview (may be empty). */
|
|
81
|
+
excerpt: string
|
|
82
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Issue-tracker selection shapes, mirroring `@cat-factory/contracts` (tracker.ts).
|
|
2
|
+
// A workspace designates one tracker — GitHub Issues or Jira — where the tech-debt
|
|
3
|
+
// recurring pipeline files its ticket before implementation starts.
|
|
4
|
+
|
|
5
|
+
export type TrackerKind = 'github' | 'jira'
|
|
6
|
+
|
|
7
|
+
export interface TrackerSettings {
|
|
8
|
+
/** The selected tracker, or null when none is configured. */
|
|
9
|
+
tracker: TrackerKind | null
|
|
10
|
+
/** Jira project key new tickets are filed under (e.g. 'ENG'); null unless Jira. */
|
|
11
|
+
jiraProjectKey: string | null
|
|
12
|
+
updatedAt: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PutTrackerSettingsInput {
|
|
16
|
+
tracker: TrackerKind | null
|
|
17
|
+
jiraProjectKey?: string | null
|
|
18
|
+
}
|