@cat-factory/app 0.9.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/app/components/board/AddTaskModal.vue +209 -21
  2. package/app/components/board/nodes/BlockNode.vue +2 -2
  3. package/app/components/focus/BlockFocusView.vue +2 -2
  4. package/app/components/layout/CommandBar.vue +7 -33
  5. package/app/components/layout/IntegrationsHub.vue +230 -0
  6. package/app/components/layout/SideBar.vue +8 -170
  7. package/app/components/panels/GenericStructuredResultView.vue +131 -0
  8. package/app/components/panels/InspectorPanel.vue +6 -2
  9. package/app/components/panels/StepResultViewHost.vue +4 -0
  10. package/app/components/panels/inspector/ServiceReleaseHealthConfig.vue +148 -0
  11. package/app/components/settings/IssueTrackerWritebackPanel.vue +45 -57
  12. package/app/components/settings/MergeThresholdsPanel.vue +189 -226
  13. package/app/components/settings/ObservabilityConnectionPanel.vue +151 -0
  14. package/app/components/settings/ServiceFragmentDefaultsPanel.vue +46 -61
  15. package/app/components/settings/WorkspaceSettingsPanel.vue +136 -63
  16. package/app/composables/api/releaseHealth.ts +11 -10
  17. package/app/pages/index.vue +4 -8
  18. package/app/stores/agents.ts +27 -2
  19. package/app/stores/releaseHealth.ts +48 -12
  20. package/app/stores/ui.ts +34 -42
  21. package/app/stores/workspace.ts +4 -0
  22. package/app/types/domain.ts +33 -1
  23. package/app/types/execution.ts +6 -0
  24. package/app/types/releaseHealth.ts +19 -11
  25. package/app/utils/catalog.spec.ts +10 -0
  26. package/app/utils/catalog.ts +20 -6
  27. package/package.json +1 -1
  28. package/app/components/board/ContextPicker.vue +0 -367
  29. package/app/components/settings/DatadogPanel.vue +0 -213
package/app/stores/ui.ts CHANGED
@@ -64,18 +64,22 @@ export const useUiStore = defineStore('ui', () => {
64
64
  // Command bar (⌘K) — searchable launcher for every navbar action.
65
65
  const commandBarOpen = ref(false)
66
66
 
67
- // Workspace-settings panels: merge-threshold preset library + per-agent-kind
68
- // default model overrides.
69
- const mergeThresholdsOpen = ref(false)
70
- // Workspace-settings panel: the run-timing escalation threshold + per-service task limit.
67
+ // Integrations hub: a single modal listing every external system the workspace
68
+ // can enable/link (GitHub, Slack, document + task sources, Datadog, LLM vendors,
69
+ // local runners, OpenRouter). Replaces the per-integration navbar buttons; each
70
+ // row inside it opens that integration's own panel via the handlers below.
71
+ const integrationsOpen = ref(false)
72
+
73
+ // Workspace-settings modal: a single tabbed window gathering the workspace-wide
74
+ // config (workspace / merge thresholds / issue writeback / service best practices).
75
+ // `workspaceSettingsTab` lets other surfaces deep-link straight to a tab.
71
76
  const workspaceSettingsOpen = ref(false)
72
- const datadogOpen = ref(false)
73
- // Workspace-settings panel: issue-tracker writeback toggles (comment on PR open,
74
- // close linked issue on merge).
75
- const issueWritebackOpen = ref(false)
77
+ const workspaceSettingsTab = ref('workspace')
78
+ // Observability integration: the post-release-health connection panel (Datadog
79
+ // today, pluggable). NB: distinct from `observabilityInstanceId` below, which is the
80
+ // LLM per-call observability panel.
81
+ const observabilityConnectionOpen = ref(false)
76
82
  const modelConfigOpen = ref(false)
77
- // Workspace-settings panel: the default service-fragment selection new services inherit.
78
- const serviceFragmentDefaultsOpen = ref(false)
79
83
  // LLM-vendor subscription credentials (the token pool powering the Claude Code
80
84
  // / Codex harnesses).
81
85
  const vendorCredentialsOpen = ref(false)
@@ -280,29 +284,27 @@ export const useUiStore = defineStore('ui', () => {
280
284
  function toggleCommandBar() {
281
285
  commandBarOpen.value = !commandBarOpen.value
282
286
  }
283
- function openMergeThresholds() {
284
- mergeThresholdsOpen.value = true
287
+ function openIntegrations() {
288
+ integrationsOpen.value = true
285
289
  }
286
- function closeMergeThresholds() {
287
- mergeThresholdsOpen.value = false
290
+ function closeIntegrations() {
291
+ integrationsOpen.value = false
288
292
  }
289
- function openWorkspaceSettings() {
293
+ function openWorkspaceSettings(tab = 'workspace') {
294
+ workspaceSettingsTab.value = tab
290
295
  workspaceSettingsOpen.value = true
291
296
  }
292
297
  function closeWorkspaceSettings() {
293
298
  workspaceSettingsOpen.value = false
294
299
  }
295
- function openIssueWriteback() {
296
- issueWritebackOpen.value = true
297
- }
298
- function closeIssueWriteback() {
299
- issueWritebackOpen.value = false
300
+ function setWorkspaceSettingsTab(tab: string) {
301
+ workspaceSettingsTab.value = tab
300
302
  }
301
- function openDatadog() {
302
- datadogOpen.value = true
303
+ function openObservabilityConnection() {
304
+ observabilityConnectionOpen.value = true
303
305
  }
304
- function closeDatadog() {
305
- datadogOpen.value = false
306
+ function closeObservabilityConnection() {
307
+ observabilityConnectionOpen.value = false
306
308
  }
307
309
  function openModelConfig() {
308
310
  modelConfigOpen.value = true
@@ -310,12 +312,6 @@ export const useUiStore = defineStore('ui', () => {
310
312
  function closeModelConfig() {
311
313
  modelConfigOpen.value = false
312
314
  }
313
- function openServiceFragmentDefaults() {
314
- serviceFragmentDefaultsOpen.value = true
315
- }
316
- function closeServiceFragmentDefaults() {
317
- serviceFragmentDefaultsOpen.value = false
318
- }
319
315
  function openVendorCredentials() {
320
316
  vendorCredentialsOpen.value = true
321
317
  }
@@ -376,12 +372,11 @@ export const useUiStore = defineStore('ui', () => {
376
372
  slackOpen,
377
373
  fragmentLibraryOpen,
378
374
  commandBarOpen,
379
- mergeThresholdsOpen,
380
- issueWritebackOpen,
375
+ integrationsOpen,
381
376
  workspaceSettingsOpen,
382
- datadogOpen,
377
+ workspaceSettingsTab,
378
+ observabilityConnectionOpen,
383
379
  modelConfigOpen,
384
- serviceFragmentDefaultsOpen,
385
380
  vendorCredentialsOpen,
386
381
  localModelsOpen,
387
382
  openRouterOpen,
@@ -428,18 +423,15 @@ export const useUiStore = defineStore('ui', () => {
428
423
  openCommandBar,
429
424
  closeCommandBar,
430
425
  toggleCommandBar,
431
- openMergeThresholds,
432
- closeMergeThresholds,
433
- openIssueWriteback,
434
- closeIssueWriteback,
426
+ openIntegrations,
427
+ closeIntegrations,
435
428
  openWorkspaceSettings,
436
429
  closeWorkspaceSettings,
437
- openDatadog,
438
- closeDatadog,
430
+ setWorkspaceSettingsTab,
431
+ openObservabilityConnection,
432
+ closeObservabilityConnection,
439
433
  openModelConfig,
440
434
  closeModelConfig,
441
- openServiceFragmentDefaults,
442
- closeServiceFragmentDefaults,
443
435
  openVendorCredentials,
444
436
  closeVendorCredentials,
445
437
  openLocalModels,
@@ -14,6 +14,7 @@ import { useModelPresetsStore } from '~/stores/modelPresets'
14
14
  import { useServiceFragmentDefaultsStore } from '~/stores/serviceFragmentDefaults'
15
15
  import { useRecurringPipelinesStore } from '~/stores/recurringPipelines'
16
16
  import { useServicesStore } from '~/stores/services'
17
+ import { useAgentsStore } from '~/stores/agents'
17
18
  import { useTrackerStore } from '~/stores/tracker'
18
19
 
19
20
  /**
@@ -76,6 +77,9 @@ export const useWorkspaceStore = defineStore(
76
77
  useRecurringPipelinesStore().hydrate(snapshot.recurringPipelines ?? [])
77
78
  useTrackerStore().hydrate(snapshot.trackerSettings)
78
79
  useServicesStore().hydrate(snapshot.mounts ?? [], snapshot.serviceCatalog ?? [])
80
+ // Merge the deployment's registered custom agent kinds into the palette catalog so a
81
+ // proprietary kind renders as a first-class block + result view (idempotent on reload).
82
+ useAgentsStore().registerCustomKinds(snapshot.customAgentKinds ?? [])
79
83
  }
80
84
 
81
85
  /** Resolve accounts + boards, then open the right board for the active account. */
@@ -35,7 +35,13 @@ export type BlockStatus =
35
35
  | 'pr_ready' // pipeline finished — a PR is open and awaiting merge
36
36
  | 'done' // PR merged, implementation complete
37
37
 
38
- /** Kind of architecture building block (drives icon + accent). */
38
+ /**
39
+ * Kind of architecture building block (drives icon + accent). `external` and
40
+ * `environment` are no longer user-creatable, but the backend still emits them
41
+ * (the seed's third-party `external` service, and the environments integration's
42
+ * `environment` blocks), so they remain part of the union for display parity with
43
+ * the contracts `blockTypeSchema`.
44
+ */
39
45
  export type BlockType =
40
46
  | 'frontend'
41
47
  | 'service'
@@ -294,6 +300,26 @@ export interface AgentArchetype {
294
300
  resultView?: string
295
301
  }
296
302
 
303
+ /**
304
+ * A registered CUSTOM agent kind carried in the workspace snapshot: its id + display
305
+ * metadata + whether it runs in a container. The SPA merges these into its palette
306
+ * catalog (`registerCustomKinds`) so a deployment's proprietary kind becomes a
307
+ * first-class palette block + result view. Mirrors `CustomAgentKind` in
308
+ * `@cat-factory/contracts`.
309
+ */
310
+ export interface CustomAgentKind {
311
+ kind: AgentKind
312
+ presentation: {
313
+ label: string
314
+ icon: string
315
+ color: string
316
+ description: string
317
+ category?: AgentCategory
318
+ resultView?: string
319
+ }
320
+ container: boolean
321
+ }
322
+
297
323
  /** A reusable, linear sequence of agents. */
298
324
  export interface Pipeline {
299
325
  id: string
@@ -409,6 +435,12 @@ export interface WorkspaceSnapshot {
409
435
  serviceCatalog?: Service[]
410
436
  /** The workspace's runtime settings (human-wait escalation threshold + task limit). */
411
437
  settings?: WorkspaceSettings
438
+ /**
439
+ * Registered custom agent kinds (kind + presentation + container flag) a deployment
440
+ * mixed in. The SPA merges these into its palette catalog so a proprietary kind becomes
441
+ * a first-class palette block + result view. Absent when none are registered.
442
+ */
443
+ customAgentKinds?: CustomAgentKind[]
412
444
  }
413
445
 
414
446
  /** How the per-service running-task limit is bucketed. Mirrors `@cat-factory/contracts`. */
@@ -300,6 +300,12 @@ export interface PipelineStep {
300
300
  consensus?: ConsensusStepConfig | null
301
301
  /** text the agent produced for this step (when LLM execution is enabled). */
302
302
  output?: string
303
+ /**
304
+ * Structured JSON a registered CUSTOM kind's agent step returned (the generic
305
+ * manifest-driven `agent` dispatch). Rendered by the `generic-structured` result view.
306
+ * Absent for built-in / prose kinds.
307
+ */
308
+ custom?: unknown
303
309
  /** identifier of the model that produced `output`, for transparency. */
304
310
  model?: string
305
311
  /** prompt-fragment library ids folded into this step (manual ∪ selector pick). */
@@ -1,18 +1,26 @@
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.
1
+ // Post-release-health (observability) settings shapes, mirroring `@cat-factory/contracts`
2
+ // (release.ts). Per-workspace observability connection (provider + credentials, write-only,
3
+ // never read back) and the per-block monitor/SLO mappings the post-release-health gate reads.
4
4
 
5
- /** What `GET /datadog/connection` returns never the secret keys. */
6
- export interface DatadogConnectionView {
5
+ /** Observability vendors a workspace can connect (extensible; Datadog today). */
6
+ export type ObservabilityProviderKind = 'datadog'
7
+
8
+ /** What `GET /observability/connection` returns — never the secret keys. */
9
+ export interface ObservabilityConnectionView {
7
10
  connected: boolean
8
- site: string | null
11
+ provider: ObservabilityProviderKind | null
12
+ /** Non-secret display fields, e.g. `{ site }` for Datadog. */
13
+ summary: Record<string, string> | null
9
14
  }
10
15
 
11
- /** Set/replace the workspace's Datadog connection. */
12
- export interface UpsertDatadogConnectionInput {
13
- site: string
14
- apiKey: string
15
- appKey: string
16
+ /** Set/replace the workspace's observability connection. */
17
+ export interface UpsertObservabilityConnectionInput {
18
+ provider: ObservabilityProviderKind
19
+ credentials: {
20
+ site: string
21
+ apiKey: string
22
+ appKey: string
23
+ }
16
24
  }
17
25
 
18
26
  /** A block's monitor/SLO mapping for the post-release-health gate. */
@@ -7,6 +7,7 @@ import {
7
7
  STATUS_META,
8
8
  SYSTEM_AGENT_META,
9
9
  agentKindMeta,
10
+ blockTypeMeta,
10
11
  uid,
11
12
  } from '~/utils/catalog'
12
13
 
@@ -37,6 +38,7 @@ const BLOCK_TYPES: BlockType[] = [
37
38
  'queue',
38
39
  'integration',
39
40
  'external',
41
+ 'environment',
40
42
  ]
41
43
  const BLOCK_STATUSES: BlockStatus[] = [
42
44
  'planned',
@@ -94,6 +96,14 @@ describe('catalog', () => {
94
96
  }
95
97
  })
96
98
 
99
+ it('blockTypeMeta returns a usable fallback for an unknown block type', () => {
100
+ expect(blockTypeMeta('totally-made-up' as BlockType)).toMatchObject({
101
+ label: expect.any(String),
102
+ icon: expect.any(String),
103
+ accent: expect.any(String),
104
+ })
105
+ })
106
+
97
107
  it('provides metadata for every block status', () => {
98
108
  for (const s of BLOCK_STATUSES) {
99
109
  expect(STATUS_META[s]).toMatchObject({
@@ -384,8 +384,10 @@ export function agentKindMeta(kind: string): AgentArchetype {
384
384
  )
385
385
  }
386
386
 
387
+ type BlockTypeMeta = { label: string; icon: string; accent: string }
388
+
387
389
  /** Visual metadata for each architecture block type. */
388
- export const BLOCK_TYPE_META: Record<BlockType, { label: string; icon: string; accent: string }> = {
390
+ export const BLOCK_TYPE_META: Record<BlockType, BlockTypeMeta> = {
389
391
  frontend: { label: 'Frontend', icon: 'i-lucide-monitor', accent: '#60a5fa' },
390
392
  service: { label: 'Service', icon: 'i-lucide-server', accent: '#a78bfa' },
391
393
  api: { label: 'API', icon: 'i-lucide-route', accent: '#22d3ee' },
@@ -396,12 +398,24 @@ export const BLOCK_TYPE_META: Record<BlockType, { label: string; icon: string; a
396
398
  icon: 'i-lucide-workflow',
397
399
  accent: '#fb923c',
398
400
  },
401
+ // Not user-creatable, but still emitted by the backend (the seeded third-party
402
+ // service and the environments integration), so they need display metadata.
399
403
  external: { label: 'External', icon: 'i-lucide-globe', accent: '#94a3b8' },
400
- environment: {
401
- label: 'Environment',
402
- icon: 'i-lucide-container',
403
- accent: '#2dd4bf',
404
- },
404
+ environment: { label: 'Environment', icon: 'i-lucide-box', accent: '#2dd4bf' },
405
+ }
406
+
407
+ const FALLBACK_BLOCK_TYPE_META: BlockTypeMeta = {
408
+ label: 'Block',
409
+ icon: 'i-lucide-box',
410
+ accent: '#94a3b8',
411
+ }
412
+
413
+ /**
414
+ * Visual metadata for a block type, with a safe fallback for any unknown/legacy
415
+ * type so the board never crashes on a type the backend introduces ahead of the SPA.
416
+ */
417
+ export function blockTypeMeta(type: BlockType): BlockTypeMeta {
418
+ return BLOCK_TYPE_META[type] ?? FALLBACK_BLOCK_TYPE_META
405
419
  }
406
420
 
407
421
  /** Color + iconography for each block status. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Reusable Nuxt layer for the Agent Architecture Board SPA (components, stores, composables, pages). Consume it from a thin deployment app via `extends: ['@cat-factory/app']` and point it at your backend with NUXT_PUBLIC_API_BASE. See deploy/frontend for an example.",
5
5
  "repository": {
6
6
  "type": "git",