@cat-factory/app 0.30.6 → 0.32.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/app/components/brainstorm/BrainstormWindow.vue +617 -0
- package/app/components/followUp/FollowUpWindow.vue +257 -0
- package/app/components/kaizen/KaizenPanel.vue +208 -0
- package/app/components/kaizen/KaizenStepStatus.vue +94 -0
- package/app/components/layout/NotificationsInbox.vue +13 -0
- package/app/components/layout/SideBar.vue +12 -0
- package/app/components/panels/AgentStepDetail.vue +6 -0
- package/app/components/panels/StepResultViewHost.vue +7 -0
- package/app/components/pipeline/PipelineBuilder.vue +21 -0
- package/app/components/pipeline/PipelineProgress.vue +59 -1
- package/app/components/settings/WorkspaceSettingsPanel.vue +20 -0
- package/app/components/slack/SlackPanel.vue +1 -0
- package/app/composables/api/followUps.ts +52 -0
- package/app/composables/api/kaizen.ts +16 -0
- package/app/composables/api/reviews.ts +68 -0
- package/app/composables/useApi.ts +4 -0
- package/app/composables/useResultView.ts +3 -1
- package/app/composables/useWorkspaceStream.ts +11 -0
- package/app/pages/index.vue +2 -0
- package/app/stores/brainstorm.ts +210 -0
- package/app/stores/followUps.ts +73 -0
- package/app/stores/kaizen.ts +101 -0
- package/app/stores/pipelines.ts +25 -0
- package/app/stores/ui.ts +64 -1
- package/app/stores/workspaceSettings.ts +1 -0
- package/app/types/brainstorm.ts +55 -0
- package/app/types/domain.ts +64 -0
- package/app/types/execution.ts +41 -0
- package/app/types/notifications.ts +1 -0
- package/app/utils/catalog.spec.ts +2 -0
- package/app/utils/catalog.ts +68 -3
- package/package.json +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { ref } from 'vue'
|
|
3
|
+
import { useApi } from '~/composables/useApi'
|
|
4
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
+
import { useExecutionStore } from '~/stores/execution'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The Follow-up companion action surface. The live item state lives on the run's Coder step
|
|
9
|
+
* (`step.followUps`) and is kept fresh by the execution stream, so the window reads items
|
|
10
|
+
* straight off the execution store — this store only wraps the decide actions (file / send
|
|
11
|
+
* back / answer / dismiss) and tracks which item is mid-action so the window can disable its
|
|
12
|
+
* buttons. The returned state is also pushed back into the execution store so the UI updates
|
|
13
|
+
* immediately even before the stream echoes the change.
|
|
14
|
+
*/
|
|
15
|
+
export const useFollowUpsStore = defineStore('followUps', () => {
|
|
16
|
+
const api = useApi()
|
|
17
|
+
const workspace = useWorkspaceStore()
|
|
18
|
+
const execution = useExecutionStore()
|
|
19
|
+
|
|
20
|
+
/** Item ids with an action in flight (drives per-row spinners / disabled buttons). */
|
|
21
|
+
const acting = ref<Set<string>>(new Set())
|
|
22
|
+
/** The last error message from an action, surfaced inline; cleared on the next action. */
|
|
23
|
+
const error = ref<string | null>(null)
|
|
24
|
+
|
|
25
|
+
function isActing(itemId: string): boolean {
|
|
26
|
+
return acting.value.has(itemId)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function mark(itemId: string, on: boolean) {
|
|
30
|
+
const next = new Set(acting.value)
|
|
31
|
+
if (on) next.add(itemId)
|
|
32
|
+
else next.delete(itemId)
|
|
33
|
+
acting.value = next
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** Run one decide action, reflecting the returned state onto the run's Coder step. */
|
|
37
|
+
async function act(
|
|
38
|
+
executionId: string,
|
|
39
|
+
itemId: string,
|
|
40
|
+
call: (ws: string) => Promise<unknown>,
|
|
41
|
+
): Promise<void> {
|
|
42
|
+
error.value = null
|
|
43
|
+
mark(itemId, true)
|
|
44
|
+
try {
|
|
45
|
+
const state = await call(workspace.requireId())
|
|
46
|
+
// Reflect the authoritative state immediately (the stream will also echo it).
|
|
47
|
+
const instance = execution.getInstance(executionId)
|
|
48
|
+
const step = instance?.steps.find((s) => s.followUps?.enabled)
|
|
49
|
+
if (step && state && typeof state === 'object') {
|
|
50
|
+
step.followUps = state as typeof step.followUps
|
|
51
|
+
}
|
|
52
|
+
} catch (e) {
|
|
53
|
+
error.value = e instanceof Error ? e.message : 'Action failed'
|
|
54
|
+
throw e
|
|
55
|
+
} finally {
|
|
56
|
+
mark(itemId, false)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const fileItem = (executionId: string, itemId: string) =>
|
|
61
|
+
act(executionId, itemId, (ws) => api.fileFollowUp(ws, executionId, itemId))
|
|
62
|
+
|
|
63
|
+
const queueItem = (executionId: string, itemId: string) =>
|
|
64
|
+
act(executionId, itemId, (ws) => api.queueFollowUp(ws, executionId, itemId))
|
|
65
|
+
|
|
66
|
+
const answerItem = (executionId: string, itemId: string, answer: string) =>
|
|
67
|
+
act(executionId, itemId, (ws) => api.answerFollowUp(ws, executionId, itemId, answer))
|
|
68
|
+
|
|
69
|
+
const dismissItem = (executionId: string, itemId: string) =>
|
|
70
|
+
act(executionId, itemId, (ws) => api.dismissFollowUp(ws, executionId, itemId))
|
|
71
|
+
|
|
72
|
+
return { acting, error, isActing, fileItem, queueItem, answerItem, dismissItem }
|
|
73
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { defineStore } from 'pinia'
|
|
2
|
+
import { computed, ref } from 'vue'
|
|
3
|
+
import type { KaizenGrading, KaizenVerifiedCombo } from '~/types/domain'
|
|
4
|
+
import { useWorkspaceStore } from '~/stores/workspace'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Kaizen state: per-run gradings (for the run-window status surface) and the
|
|
8
|
+
* workspace-wide history + verified-combo library (for the Kaizen screen). Gradings
|
|
9
|
+
* arrive both via lazy loads and live over the workspace stream (`upsert`). Never
|
|
10
|
+
* surfaced on the board — only inside run details + the dedicated screen.
|
|
11
|
+
*/
|
|
12
|
+
export const useKaizenStore = defineStore('kaizen', () => {
|
|
13
|
+
const api = useApi()
|
|
14
|
+
|
|
15
|
+
/** Gradings keyed by run (execution) id, for the run window. */
|
|
16
|
+
const byExecution = ref<Record<string, KaizenGrading[]>>({})
|
|
17
|
+
/** Recent grading history for the Kaizen screen. */
|
|
18
|
+
const history = ref<KaizenGrading[]>([])
|
|
19
|
+
/** The verified-combo library for the Kaizen screen. */
|
|
20
|
+
const verified = ref<KaizenVerifiedCombo[]>([])
|
|
21
|
+
const loadingOverview = ref(false)
|
|
22
|
+
const loadingExecution = ref<Set<string>>(new Set())
|
|
23
|
+
/** 503 ⇒ the Kaizen feature isn't configured on this deployment. */
|
|
24
|
+
const available = ref<boolean | null>(null)
|
|
25
|
+
|
|
26
|
+
function gradingsFor(executionId: string): KaizenGrading[] {
|
|
27
|
+
return byExecution.value[executionId] ?? []
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The grading for a specific step of a run, if any. */
|
|
31
|
+
function gradingForStep(executionId: string, stepIndex: number): KaizenGrading | null {
|
|
32
|
+
return gradingsFor(executionId).find((g) => g.stepIndex === stepIndex) ?? null
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async function loadOverview() {
|
|
36
|
+
const ws = useWorkspaceStore()
|
|
37
|
+
loadingOverview.value = true
|
|
38
|
+
try {
|
|
39
|
+
const overview = await api.getKaizenOverview(ws.requireId())
|
|
40
|
+
history.value = overview.gradings
|
|
41
|
+
verified.value = overview.verified
|
|
42
|
+
available.value = true
|
|
43
|
+
} catch (e) {
|
|
44
|
+
if ((e as { statusCode?: number; status?: number })?.statusCode === 503)
|
|
45
|
+
available.value = false
|
|
46
|
+
else throw e
|
|
47
|
+
} finally {
|
|
48
|
+
loadingOverview.value = false
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function loadForExecution(executionId: string) {
|
|
53
|
+
const ws = useWorkspaceStore()
|
|
54
|
+
loadingExecution.value = new Set(loadingExecution.value).add(executionId)
|
|
55
|
+
try {
|
|
56
|
+
const { gradings } = await api.getKaizenForExecution(ws.requireId(), executionId)
|
|
57
|
+
byExecution.value = { ...byExecution.value, [executionId]: gradings }
|
|
58
|
+
available.value = true
|
|
59
|
+
} catch (e) {
|
|
60
|
+
if ((e as { statusCode?: number; status?: number })?.statusCode === 503)
|
|
61
|
+
available.value = false
|
|
62
|
+
else throw e
|
|
63
|
+
} finally {
|
|
64
|
+
const next = new Set(loadingExecution.value)
|
|
65
|
+
next.delete(executionId)
|
|
66
|
+
loadingExecution.value = next
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Fold a grading pushed over the stream into both the run cache and the screen history. */
|
|
71
|
+
function upsert(grading: KaizenGrading) {
|
|
72
|
+
const current = byExecution.value[grading.executionId] ?? []
|
|
73
|
+
const replaced = current.some((g) => g.id === grading.id)
|
|
74
|
+
const nextRun = replaced
|
|
75
|
+
? current.map((g) => (g.id === grading.id ? grading : g))
|
|
76
|
+
: [...current, grading]
|
|
77
|
+
byExecution.value = { ...byExecution.value, [grading.executionId]: nextRun }
|
|
78
|
+
// Keep the screen history live too (newest first), if it's been loaded.
|
|
79
|
+
const inHistory = history.value.some((g) => g.id === grading.id)
|
|
80
|
+
if (inHistory) history.value = history.value.map((g) => (g.id === grading.id ? grading : g))
|
|
81
|
+
else history.value = [grading, ...history.value]
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const isLoadingExecution = (executionId: string) => loadingExecution.value.has(executionId)
|
|
85
|
+
const verifiedCount = computed(() => verified.value.filter((c) => c.verified).length)
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
byExecution,
|
|
89
|
+
history,
|
|
90
|
+
verified,
|
|
91
|
+
available,
|
|
92
|
+
loadingOverview,
|
|
93
|
+
verifiedCount,
|
|
94
|
+
gradingsFor,
|
|
95
|
+
gradingForStep,
|
|
96
|
+
loadOverview,
|
|
97
|
+
loadForExecution,
|
|
98
|
+
upsert,
|
|
99
|
+
isLoadingExecution,
|
|
100
|
+
}
|
|
101
|
+
})
|
package/app/stores/pipelines.ts
CHANGED
|
@@ -48,6 +48,11 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
48
48
|
const draftConsensus = ref<(ConsensusStepConfig | null)[]>([])
|
|
49
49
|
/** Per-step estimate gating, kept index-aligned with `draft` (null ⇒ always run). */
|
|
50
50
|
const draftGating = ref<(StepGating | null)[]>([])
|
|
51
|
+
/**
|
|
52
|
+
* Per-step Follow-up companion toggle, kept index-aligned with `draft`. Only meaningful on
|
|
53
|
+
* a `coder` step; `false` disables the companion there (default/true ⇒ enabled).
|
|
54
|
+
*/
|
|
55
|
+
const draftFollowUps = ref<(boolean | null)[]>([])
|
|
51
56
|
/** Organizational labels for the pipeline being assembled/edited. */
|
|
52
57
|
const draftLabels = ref<string[]>([])
|
|
53
58
|
const draftName = ref('New pipeline')
|
|
@@ -71,6 +76,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
71
76
|
draftThresholds.value.splice(index, 0, null)
|
|
72
77
|
draftConsensus.value.splice(index, 0, null)
|
|
73
78
|
draftGating.value.splice(index, 0, null)
|
|
79
|
+
draftFollowUps.value.splice(index, 0, null)
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
function addToDraft(kind: AgentKind) {
|
|
@@ -84,6 +90,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
84
90
|
draftThresholds.value.splice(index, 1)
|
|
85
91
|
draftConsensus.value.splice(index, 1)
|
|
86
92
|
draftGating.value.splice(index, 1)
|
|
93
|
+
draftFollowUps.value.splice(index, 1)
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
function moveInDraft(from: number, to: number) {
|
|
@@ -100,6 +107,8 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
100
107
|
draftConsensus.value.splice(to, 0, cons ?? null)
|
|
101
108
|
const [gat] = draftGating.value.splice(from, 1)
|
|
102
109
|
draftGating.value.splice(to, 0, gat ?? null)
|
|
110
|
+
const [fu] = draftFollowUps.value.splice(from, 1)
|
|
111
|
+
draftFollowUps.value.splice(to, 0, fu ?? null)
|
|
103
112
|
}
|
|
104
113
|
|
|
105
114
|
/** Whether the producer step at `index` currently has its companion attached after it. */
|
|
@@ -174,6 +183,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
174
183
|
draftThresholds.value = reorder(draftThresholds.value)
|
|
175
184
|
draftConsensus.value = reorder(draftConsensus.value)
|
|
176
185
|
draftGating.value = reorder(draftGating.value)
|
|
186
|
+
draftFollowUps.value = reorder(draftFollowUps.value)
|
|
177
187
|
}
|
|
178
188
|
|
|
179
189
|
/** Toggle the consensus mechanism on the draft step at `index` (default config / off). */
|
|
@@ -191,6 +201,12 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
191
201
|
draftGates.value[index] = !draftGates.value[index]
|
|
192
202
|
}
|
|
193
203
|
|
|
204
|
+
/** Toggle the Follow-up companion on the draft (coder) step at `index` (default on → off). */
|
|
205
|
+
function toggleDraftFollowUps(index: number) {
|
|
206
|
+
// Default (null/true) is enabled, so the first toggle disables it (false); toggle back to null.
|
|
207
|
+
draftFollowUps.value[index] = draftFollowUps.value[index] === false ? null : false
|
|
208
|
+
}
|
|
209
|
+
|
|
194
210
|
/** Enable/disable the draft step at `index` without removing it. */
|
|
195
211
|
function toggleDraftEnabled(index: number) {
|
|
196
212
|
draftEnabled.value[index] = draftEnabled.value[index] === false
|
|
@@ -203,6 +219,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
203
219
|
draftThresholds.value = []
|
|
204
220
|
draftConsensus.value = []
|
|
205
221
|
draftGating.value = []
|
|
222
|
+
draftFollowUps.value = []
|
|
206
223
|
draftLabels.value = []
|
|
207
224
|
draftName.value = 'New pipeline'
|
|
208
225
|
editingId.value = null
|
|
@@ -216,6 +233,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
216
233
|
draftThresholds.value = pipeline.agentKinds.map((_, i) => pipeline.thresholds?.[i] ?? null)
|
|
217
234
|
draftConsensus.value = pipeline.agentKinds.map((_, i) => pipeline.consensus?.[i] ?? null)
|
|
218
235
|
draftGating.value = pipeline.agentKinds.map((_, i) => pipeline.gating?.[i] ?? null)
|
|
236
|
+
draftFollowUps.value = pipeline.agentKinds.map((_, i) => pipeline.followUps?.[i] ?? null)
|
|
219
237
|
draftLabels.value = [...(pipeline.labels ?? [])]
|
|
220
238
|
draftName.value = pipeline.name
|
|
221
239
|
editingId.value = pipeline.id
|
|
@@ -240,6 +258,11 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
240
258
|
: {}),
|
|
241
259
|
// Only send gating when at least one step has gating enabled.
|
|
242
260
|
...(draftGating.value.some((g) => g?.enabled) ? { gating: [...draftGating.value] } : {}),
|
|
261
|
+
// Only send followUps when at least one step disables it (default is on, so only the
|
|
262
|
+
// explicit `false` opt-outs are worth persisting).
|
|
263
|
+
...(draftFollowUps.value.some((f) => f === false)
|
|
264
|
+
? { followUps: [...draftFollowUps.value] }
|
|
265
|
+
: {}),
|
|
243
266
|
// Only send labels when there are any.
|
|
244
267
|
...(draftLabels.value.length ? { labels: [...draftLabels.value] } : {}),
|
|
245
268
|
}
|
|
@@ -297,6 +320,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
297
320
|
draftThresholds,
|
|
298
321
|
draftConsensus,
|
|
299
322
|
draftGating,
|
|
323
|
+
draftFollowUps,
|
|
300
324
|
draftLabels,
|
|
301
325
|
draftName,
|
|
302
326
|
editingId,
|
|
@@ -311,6 +335,7 @@ export const usePipelinesStore = defineStore('pipelines', () => {
|
|
|
311
335
|
toggleCompanion,
|
|
312
336
|
toggleDraftGating,
|
|
313
337
|
toggleDraftGate,
|
|
338
|
+
toggleDraftFollowUps,
|
|
314
339
|
toggleDraftEnabled,
|
|
315
340
|
toggleDraftConsensus,
|
|
316
341
|
setDraftConsensus,
|
package/app/stores/ui.ts
CHANGED
|
@@ -135,6 +135,10 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
135
135
|
blockId: string
|
|
136
136
|
instanceId: string | null
|
|
137
137
|
stepIndex: number | null
|
|
138
|
+
// The brainstorm dialogue stage, set only when `view === 'brainstorm'` (its two agent
|
|
139
|
+
// kinds share one window). Derived from the step's agent kind on the pipeline path, or
|
|
140
|
+
// passed explicitly on an off-path open.
|
|
141
|
+
stage?: 'requirements' | 'architecture'
|
|
138
142
|
} | null>(null)
|
|
139
143
|
|
|
140
144
|
// Agent step-detail overlay: which pipeline step (a run instance + step index)
|
|
@@ -149,6 +153,10 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
149
153
|
// per-call detail from the observability store on open.
|
|
150
154
|
const observabilityInstanceId = ref<string | null>(null)
|
|
151
155
|
|
|
156
|
+
// The Kaizen screen (grading history + verified-combo library), a full-panel overlay
|
|
157
|
+
// opened from the sidebar. Distinct from the per-run grading status shown in run details.
|
|
158
|
+
const kaizenScreenOpen = ref(false)
|
|
159
|
+
|
|
152
160
|
/** Current canvas zoom (driven by Vue Flow viewport). */
|
|
153
161
|
const zoom = ref(1)
|
|
154
162
|
|
|
@@ -229,7 +237,20 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
229
237
|
? agentKindMeta(step.agentKind).resultView
|
|
230
238
|
: undefined
|
|
231
239
|
if (view && instance) {
|
|
232
|
-
|
|
240
|
+
// The brainstorm window is shared by both stages; carry which one from the step's kind.
|
|
241
|
+
const stage =
|
|
242
|
+
view === 'brainstorm'
|
|
243
|
+
? step?.agentKind === 'architecture-brainstorm'
|
|
244
|
+
? 'architecture'
|
|
245
|
+
: 'requirements'
|
|
246
|
+
: undefined
|
|
247
|
+
resultView.value = {
|
|
248
|
+
view,
|
|
249
|
+
blockId: instance.blockId,
|
|
250
|
+
instanceId,
|
|
251
|
+
stepIndex,
|
|
252
|
+
...(stage ? { stage } : {}),
|
|
253
|
+
}
|
|
233
254
|
return
|
|
234
255
|
}
|
|
235
256
|
stepDetail.value = { instanceId, stepIndex }
|
|
@@ -452,10 +473,41 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
452
473
|
function openClarityReview(blockId: string) {
|
|
453
474
|
resultView.value = { view: 'clarity-review', blockId, instanceId: null, stepIndex: null }
|
|
454
475
|
}
|
|
476
|
+
function openBrainstorm(blockId: string, stage: 'requirements' | 'architecture') {
|
|
477
|
+
resultView.value = { view: 'brainstorm', blockId, instanceId: null, stepIndex: null, stage }
|
|
478
|
+
}
|
|
455
479
|
// Open the service-spec window for a service frame (the inspector's "View Requirements").
|
|
456
480
|
function openServiceSpec(blockId: string) {
|
|
457
481
|
resultView.value = { view: 'service-spec', blockId, instanceId: null, stepIndex: null }
|
|
458
482
|
}
|
|
483
|
+
// Open the Follow-up companion window for a run's Coder step (the blinking chip + the
|
|
484
|
+
// `followup_pending` notification). Resolves the Coder step index from the run when not
|
|
485
|
+
// given, so callers that only know the run can still open it.
|
|
486
|
+
function openFollowUps(instanceId: string, stepIndex: number | null = null) {
|
|
487
|
+
const execution = useExecutionStore()
|
|
488
|
+
const instance = execution.getInstance(instanceId)
|
|
489
|
+
if (!instance) return
|
|
490
|
+
// A pipeline may carry more than one follow-up-enabled Coder step, so don't blindly pick
|
|
491
|
+
// the first when no index is given: prefer the step that still has undecided items (the
|
|
492
|
+
// one the run is parked on), else the current step, else the first enabled one.
|
|
493
|
+
const resolveIdx = () => {
|
|
494
|
+
const pending = instance.steps.findIndex(
|
|
495
|
+
(s) => s.followUps?.enabled && s.followUps.items.some((i) => i.status === 'pending'),
|
|
496
|
+
)
|
|
497
|
+
if (pending >= 0) return pending
|
|
498
|
+
const current = instance.steps[instance.currentStep]
|
|
499
|
+
if (current?.followUps?.enabled) return instance.currentStep
|
|
500
|
+
return instance.steps.findIndex((s) => s.followUps?.enabled)
|
|
501
|
+
}
|
|
502
|
+
const idx = stepIndex ?? resolveIdx()
|
|
503
|
+
if (idx < 0) return
|
|
504
|
+
resultView.value = {
|
|
505
|
+
view: 'follow-ups',
|
|
506
|
+
blockId: instance.blockId,
|
|
507
|
+
instanceId,
|
|
508
|
+
stepIndex: idx,
|
|
509
|
+
}
|
|
510
|
+
}
|
|
459
511
|
function closeResultView() {
|
|
460
512
|
resultView.value = null
|
|
461
513
|
}
|
|
@@ -473,6 +525,12 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
473
525
|
function closeObservability() {
|
|
474
526
|
observabilityInstanceId.value = null
|
|
475
527
|
}
|
|
528
|
+
function openKaizen() {
|
|
529
|
+
kaizenScreenOpen.value = true
|
|
530
|
+
}
|
|
531
|
+
function closeKaizen() {
|
|
532
|
+
kaizenScreenOpen.value = false
|
|
533
|
+
}
|
|
476
534
|
|
|
477
535
|
return {
|
|
478
536
|
selectedBlockId,
|
|
@@ -513,6 +571,7 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
513
571
|
closeResultView,
|
|
514
572
|
stepDetail,
|
|
515
573
|
observabilityInstanceId,
|
|
574
|
+
kaizenScreenOpen,
|
|
516
575
|
zoom,
|
|
517
576
|
lod,
|
|
518
577
|
expandedFrames,
|
|
@@ -583,11 +642,15 @@ export const useUiStore = defineStore('ui', () => {
|
|
|
583
642
|
resetAiOnboarding,
|
|
584
643
|
openRequirementReview,
|
|
585
644
|
openClarityReview,
|
|
645
|
+
openBrainstorm,
|
|
586
646
|
openServiceSpec,
|
|
647
|
+
openFollowUps,
|
|
587
648
|
closeRequirementReview,
|
|
588
649
|
openStepDetail,
|
|
589
650
|
closeStepDetail,
|
|
590
651
|
openObservability,
|
|
591
652
|
closeObservability,
|
|
653
|
+
openKaizen,
|
|
654
|
+
closeKaizen,
|
|
592
655
|
}
|
|
593
656
|
})
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// Brainstorm (structured-dialogue) wire types. Mirror of `@cat-factory/contracts`'
|
|
2
|
+
// brainstorm.ts, kept in sync by hand like the rest of `~/types/*` (the SPA does not import
|
|
3
|
+
// the backend package directly).
|
|
4
|
+
//
|
|
5
|
+
// A brainstorm agent runs a structured dialogue: it PROPOSES options with explicit
|
|
6
|
+
// trade-offs (raised as review items), a human picks / steers / dismisses, and the picks are
|
|
7
|
+
// folded into ONE converged direction. There are two stages (`requirements`, `architecture`)
|
|
8
|
+
// served by one engine; a block may have one live session per stage.
|
|
9
|
+
//
|
|
10
|
+
// Structurally identical to a requirements review (the items share the same shape), so the
|
|
11
|
+
// per-item types are reused from `~/types/requirements`; only the `stage` discriminator and
|
|
12
|
+
// the converged document (`convergedDirection`) differ.
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
RequirementReviewItem,
|
|
16
|
+
ReviewItemCategory,
|
|
17
|
+
ReviewItemSeverity,
|
|
18
|
+
ReviewItemStatus,
|
|
19
|
+
} from '~/types/requirements'
|
|
20
|
+
|
|
21
|
+
export type { ReviewItemCategory, ReviewItemSeverity, ReviewItemStatus }
|
|
22
|
+
|
|
23
|
+
/** Which dialogue a brainstorm session drives. */
|
|
24
|
+
export type BrainstormStage = 'requirements' | 'architecture'
|
|
25
|
+
|
|
26
|
+
/** A brainstorm option is the same shape as a requirements-review item. */
|
|
27
|
+
export type BrainstormItem = RequirementReviewItem
|
|
28
|
+
|
|
29
|
+
/** Lifecycle of a brainstorm session — identical to the requirements review lifecycle. */
|
|
30
|
+
export type BrainstormStatus =
|
|
31
|
+
| 'ready'
|
|
32
|
+
| 'incorporating'
|
|
33
|
+
| 'reviewing'
|
|
34
|
+
| 'merged'
|
|
35
|
+
| 'exceeded'
|
|
36
|
+
| 'incorporated'
|
|
37
|
+
|
|
38
|
+
/** How a human resolves a session that hit its iteration cap. */
|
|
39
|
+
export type ResolveBrainstormExceededChoice = 'extra-round' | 'proceed' | 'stop-reset'
|
|
40
|
+
|
|
41
|
+
export interface BrainstormSession {
|
|
42
|
+
id: string
|
|
43
|
+
blockId: string
|
|
44
|
+
stage: BrainstormStage
|
|
45
|
+
status: BrainstormStatus
|
|
46
|
+
items: BrainstormItem[]
|
|
47
|
+
model: string | null
|
|
48
|
+
convergedDirection: string | null
|
|
49
|
+
/** Agent passes run so far (initial pass is 1; each re-run adds one). */
|
|
50
|
+
iteration: number
|
|
51
|
+
/** The agent-pass budget (from the task's merge preset; an extra round bumps it). */
|
|
52
|
+
maxIterations: number
|
|
53
|
+
createdAt: number
|
|
54
|
+
updatedAt: number
|
|
55
|
+
}
|
package/app/types/domain.ts
CHANGED
|
@@ -20,6 +20,7 @@ import type { Notification } from './notifications'
|
|
|
20
20
|
import type { RequirementReview } from './requirements'
|
|
21
21
|
import type { ConsensusSession, ConsensusStepConfig, StepGating, TaskEstimate } from './consensus'
|
|
22
22
|
import type { ClarityReview } from './clarity'
|
|
23
|
+
import type { BrainstormSession } from './brainstorm'
|
|
23
24
|
import type { MergeThresholdPreset } from './merge'
|
|
24
25
|
import type { ModelPreset } from './model-presets'
|
|
25
26
|
import type { PipelineSchedule } from './recurring'
|
|
@@ -256,6 +257,11 @@ export interface TestReport {
|
|
|
256
257
|
/** The kinds of agents available in the agent palette. */
|
|
257
258
|
export type AgentKind =
|
|
258
259
|
| 'requirements-review'
|
|
260
|
+
// Brainstorm (structured-dialogue) gates: propose options with trade-offs and let the human
|
|
261
|
+
// converge. `requirements-brainstorm` runs before the requirements review; `architecture-
|
|
262
|
+
// brainstorm` before the architect. Both open the shared brainstorm window.
|
|
263
|
+
| 'requirements-brainstorm'
|
|
264
|
+
| 'architecture-brainstorm'
|
|
259
265
|
| 'architect'
|
|
260
266
|
| 'researcher'
|
|
261
267
|
| 'coder'
|
|
@@ -309,6 +315,9 @@ export type AgentKind =
|
|
|
309
315
|
// validate the change in a live URL, dispatching the Tester's `fixer` (from findings) or
|
|
310
316
|
// the `conflict-resolver` (on a conflicting pull-main) on demand. Opens its own window.
|
|
311
317
|
| 'human-test'
|
|
318
|
+
// The Kaizen agent: post-run grader (NOT a pipeline step / palette archetype). Surfaced
|
|
319
|
+
// only in Model Configuration (its model is pinnable like any agent) and run details.
|
|
320
|
+
| 'kaizen'
|
|
312
321
|
|
|
313
322
|
/** A draggable agent definition shown in the agent palette. */
|
|
314
323
|
/** Palette grouping for the agent archetypes (collapsible sections in the builder). */
|
|
@@ -387,6 +396,12 @@ export interface Pipeline {
|
|
|
387
396
|
* always run. Used to make a companion conditional on the task estimate.
|
|
388
397
|
*/
|
|
389
398
|
gating?: (StepGating | null)[]
|
|
399
|
+
/**
|
|
400
|
+
* Per-step Follow-up companion toggle, parallel to `agentKinds`: governs whether a `coder`
|
|
401
|
+
* step runs the future-looking Follow-up companion. `followUps[i] === false` disables it;
|
|
402
|
+
* `null`/`true`/absent ⇒ enabled (a Coder step gets it by default). Ignored on non-coder steps.
|
|
403
|
+
*/
|
|
404
|
+
followUps?: (boolean | null)[]
|
|
390
405
|
/** Free-form organizational labels for the library (filter/search). */
|
|
391
406
|
labels?: string[]
|
|
392
407
|
/** True when archived: kept but hidden from the default library view. */
|
|
@@ -495,6 +510,8 @@ export interface WorkspaceSettings {
|
|
|
495
510
|
taskLimitPerType: Partial<Record<CreateTaskType, number>> | null
|
|
496
511
|
/** Whether to store the complete provided-context snapshot for each container agent. */
|
|
497
512
|
storeAgentContext: boolean
|
|
513
|
+
/** Whether the Kaizen agent grades agent steps after each run. On by default. */
|
|
514
|
+
kaizenEnabled: boolean
|
|
498
515
|
/** Spend budget currency (ISO 4217). Null ⇒ the built-in default (`EUR`). */
|
|
499
516
|
spendCurrency: string | null
|
|
500
517
|
/** Monthly spend budget in `spendCurrency`. Null ⇒ the built-in default. */
|
|
@@ -508,6 +525,7 @@ export interface UpdateWorkspaceSettingsInput {
|
|
|
508
525
|
taskLimitShared?: number | null
|
|
509
526
|
taskLimitPerType?: Partial<Record<CreateTaskType, number>> | null
|
|
510
527
|
storeAgentContext?: boolean
|
|
528
|
+
kaizenEnabled?: boolean
|
|
511
529
|
spendCurrency?: string | null
|
|
512
530
|
spendMonthlyLimit?: number | null
|
|
513
531
|
}
|
|
@@ -520,6 +538,50 @@ export interface ServiceFragmentDefaults {
|
|
|
520
538
|
fragmentIds: string[]
|
|
521
539
|
}
|
|
522
540
|
|
|
541
|
+
/** Lifecycle of a Kaizen grading. Mirrors `@cat-factory/contracts`. */
|
|
542
|
+
export type KaizenGradingStatus = 'scheduled' | 'running' | 'complete' | 'failed'
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* A Kaizen grading of one completed agent step (how smooth/efficient the interaction
|
|
546
|
+
* was, 1..5, plus recommendations). Mirrors `@cat-factory/contracts`.
|
|
547
|
+
*/
|
|
548
|
+
export interface KaizenGrading {
|
|
549
|
+
id: string
|
|
550
|
+
executionId: string
|
|
551
|
+
blockId: string
|
|
552
|
+
stepIndex: number
|
|
553
|
+
agentKind: string
|
|
554
|
+
model: string
|
|
555
|
+
promptVersion: number
|
|
556
|
+
comboKey: string
|
|
557
|
+
status: KaizenGradingStatus
|
|
558
|
+
grade: number | null
|
|
559
|
+
summary: string
|
|
560
|
+
recommendations: string[]
|
|
561
|
+
graderModel: string | null
|
|
562
|
+
error: string | null
|
|
563
|
+
createdAt: number
|
|
564
|
+
updatedAt: number
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/** A `(promptVersion, agentKind, model)` combo's verification progress. */
|
|
568
|
+
export interface KaizenVerifiedCombo {
|
|
569
|
+
comboKey: string
|
|
570
|
+
agentKind: string
|
|
571
|
+
model: string
|
|
572
|
+
promptVersion: number
|
|
573
|
+
consecutiveHighGrades: number
|
|
574
|
+
verified: boolean
|
|
575
|
+
verifiedAt: number | null
|
|
576
|
+
updatedAt: number
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/** The Kaizen screen payload: recent grading history + the verified-combo library. */
|
|
580
|
+
export interface KaizenOverview {
|
|
581
|
+
gradings: KaizenGrading[]
|
|
582
|
+
verified: KaizenVerifiedCombo[]
|
|
583
|
+
}
|
|
584
|
+
|
|
523
585
|
/**
|
|
524
586
|
* Real-time events pushed over the workspace WebSocket stream (see
|
|
525
587
|
* `useWorkspaceStream`). Mirrors `WorkspaceEvent` in `@cat-factory/contracts`.
|
|
@@ -533,6 +595,8 @@ export type WorkspaceEvent =
|
|
|
533
595
|
| { type: 'requirements'; review: RequirementReview; at: number }
|
|
534
596
|
| { type: 'consensus'; session: ConsensusSession; at: number }
|
|
535
597
|
| { type: 'clarity'; review: ClarityReview; at: number }
|
|
598
|
+
| { type: 'brainstorm'; session: BrainstormSession; at: number }
|
|
599
|
+
| { type: 'kaizen'; grading: KaizenGrading; at: number }
|
|
536
600
|
|
|
537
601
|
/** Level-of-detail buckets driven by the canvas zoom level. Shallow → deep:
|
|
538
602
|
* `far`/`mid`/`close` govern a service frame (chip → card → opened with tasks);
|
package/app/types/execution.ts
CHANGED
|
@@ -382,6 +382,47 @@ export interface PipelineStep {
|
|
|
382
382
|
* Mirrors `runEnvironmentSchema`.
|
|
383
383
|
*/
|
|
384
384
|
environment?: RunEnvironment | null
|
|
385
|
+
/**
|
|
386
|
+
* Live Follow-up companion state when this (coder) step has the future-looking companion
|
|
387
|
+
* enabled: the forward-looking items the Coder streamed (loose ends / side-tasks /
|
|
388
|
+
* questions) and the send-back loop budget. The chip blinks while any item is `pending`;
|
|
389
|
+
* the gate holds the pipeline until every item is decided. Mirrors `followUpsStepStateSchema`.
|
|
390
|
+
*/
|
|
391
|
+
followUps?: FollowUpsStepState | null
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/** What a streamed item is: a forward-looking follow-up or a clarifying question. */
|
|
395
|
+
export type FollowUpItemKind = 'follow_up' | 'question'
|
|
396
|
+
|
|
397
|
+
/** Lifecycle of a single follow-up / question item (mirrors `followUpItemStatusSchema`). */
|
|
398
|
+
export type FollowUpItemStatus = 'pending' | 'filed' | 'queued' | 'answered' | 'dismissed'
|
|
399
|
+
|
|
400
|
+
/** One forward-looking item the Coder surfaced (mirrors `followUpItemSchema`). */
|
|
401
|
+
export interface FollowUpItem {
|
|
402
|
+
id: string
|
|
403
|
+
kind: FollowUpItemKind
|
|
404
|
+
title: string
|
|
405
|
+
detail: string
|
|
406
|
+
suggestedAction?: string | null
|
|
407
|
+
status: FollowUpItemStatus
|
|
408
|
+
/** The human's answer to a `question` item, or null while unanswered / not a question. */
|
|
409
|
+
answer?: string | null
|
|
410
|
+
/** Canonical external id of the filed ticket (e.g. "owner/repo#123"), when `filed`. */
|
|
411
|
+
ticketExternalId?: string | null
|
|
412
|
+
/** URL of the filed ticket, when `filed`. */
|
|
413
|
+
ticketUrl?: string | null
|
|
414
|
+
/** True once a queued / answered item was folded into a Coder loop-back. */
|
|
415
|
+
sentToCoder?: boolean
|
|
416
|
+
createdAt: number
|
|
417
|
+
updatedAt: number
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/** Live Follow-up companion state on the Coder step (mirrors `followUpsStepStateSchema`). */
|
|
421
|
+
export interface FollowUpsStepState {
|
|
422
|
+
enabled: boolean
|
|
423
|
+
items: FollowUpItem[]
|
|
424
|
+
loops?: number
|
|
425
|
+
maxLoops?: number
|
|
385
426
|
}
|
|
386
427
|
|
|
387
428
|
/** One failing CI check the gate's precheck saw (mirrors `gateFailingCheckSchema`). */
|
|
@@ -15,6 +15,7 @@ export type NotificationType =
|
|
|
15
15
|
| 'release_regression'
|
|
16
16
|
| 'decision_required'
|
|
17
17
|
| 'human_test_ready'
|
|
18
|
+
| 'followup_pending'
|
|
18
19
|
export type NotificationStatus = 'open' | 'acted' | 'dismissed'
|
|
19
20
|
|
|
20
21
|
/** The on-call agent's recommendation on a `release_regression`. */
|