@cat-factory/app 0.133.0 → 0.135.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/environments/EnvironmentSetupWizard.vue +70 -632
  2. package/app/components/environments/steps/EnvPickStep.vue +40 -0
  3. package/app/components/environments/steps/EnvPreflightStep.vue +115 -0
  4. package/app/components/environments/steps/EnvReviewStep.vue +381 -0
  5. package/app/components/environments/steps/EnvSaveStep.vue +109 -0
  6. package/app/components/environments/steps/JourneyStepNav.vue +30 -0
  7. package/app/components/panels/StepResultViewHost.vue +48 -86
  8. package/app/modular/agent-kinds.spec.ts +80 -0
  9. package/app/modular/agent-kinds.ts +83 -0
  10. package/app/modular/journeys/environmentSetup.frame.ts +31 -0
  11. package/app/modular/journeys/environmentSetup.logic.spec.ts +34 -0
  12. package/app/modular/journeys/environmentSetup.logic.ts +86 -0
  13. package/app/modular/journeys/environmentSetup.ts +143 -0
  14. package/app/modular/journeys/persistence.spec.ts +68 -0
  15. package/app/modular/journeys/persistence.ts +58 -0
  16. package/app/modular/nav-contributions.spec.ts +1 -1
  17. package/app/modular/nav-contributions.ts +7 -11
  18. package/app/modular/registry.spec.ts +38 -0
  19. package/app/modular/registry.ts +30 -13
  20. package/app/modular/result-views.ts +104 -0
  21. package/app/modular/slots.ts +40 -0
  22. package/app/plugins/modular.client.ts +59 -3
  23. package/app/stores/agents.spec.ts +100 -0
  24. package/app/stores/agents.ts +115 -34
  25. package/app/stores/environmentWizard.ts +23 -27
  26. package/app/stores/workspace.ts +4 -3
  27. package/app/types/domain.ts +4 -3
  28. package/app/utils/catalog.ts +57 -16
  29. package/package.json +13 -12
@@ -1,21 +1,96 @@
1
1
  import { defineStore } from 'pinia'
2
- import { ref } from 'vue'
3
- import { AGENT_ARCHETYPES, AGENT_BY_KIND, uid } from '~/utils/catalog'
2
+ import { computed, ref, watch } from 'vue'
3
+ import type { RemoteModuleManifest } from '@modular-vue/core'
4
+ import { buildAgentCapabilitiesManifest, customKindToArchetype } from '~/modular/agent-kinds'
5
+ import type { AppSlots } from '~/modular/slots'
6
+ import {
7
+ AGENT_ARCHETYPES,
8
+ AGENT_BY_KIND,
9
+ setCustomAgentKindMeta,
10
+ SYSTEM_AGENT_META,
11
+ uid,
12
+ } from '~/utils/catalog'
4
13
  import type { AgentArchetype, AgentKind, CustomAgentKind } from '~/types/domain'
5
14
 
6
15
  /**
7
- * The agent palette. Seeded from the static catalog, but custom agents can be
8
- * added at runtime (they show up in the pipeline builder). Newly created agents
9
- * are also registered into AGENT_BY_KIND so the many components that look an
10
- * agent up by kind keep rendering it correctly.
16
+ * The agent palette catalog (slice 2 of the modular-vue adoption
17
+ * docs/initiatives/modular-vue-adoption.md).
18
+ *
19
+ * Reactive union of three sources, none of which mutates the frozen built-in
20
+ * {@link AGENT_BY_KIND} const any more:
21
+ * - the built-in archetypes (static);
22
+ * - CONSUMER-shipped kinds contributed as CODE via the modular `agentKinds`
23
+ * slot (`registerConsumerKinds`, fed once at boot from the resolved manifest);
24
+ * - the deployment's BACKEND-registered kinds, modeled as a single
25
+ * {@link RemoteModuleManifest} swapped per workspace snapshot
26
+ * (`hydrateCustomKinds`) and read directly (single-active-manifest shape).
27
+ *
28
+ * The merged custom catalog is projected back into `catalog.ts`'s
29
+ * {@link setCustomAgentKindMeta} read-model so the pure `agentKindMeta` /
30
+ * `isKnownAgentKind` lookups (used across ~17 renderers) resolve a custom kind
31
+ * reactively without importing this store.
11
32
  */
12
33
  export const useAgentsStore = defineStore('agents', () => {
13
- const archetypes = ref<AgentArchetype[]>([...AGENT_ARCHETYPES])
34
+ // CODE-shipped consumer kinds from the static `agentKinds` slot (fed once at
35
+ // boot by the modular install plugin — module slots are resolved once).
36
+ const consumerKinds = ref<CustomAgentKind[]>([])
37
+ // The active per-workspace capability manifest built from the snapshot's
38
+ // `customAgentKinds`, or null before the first hydrate.
39
+ const capabilitiesManifest = ref<RemoteModuleManifest<AppSlots> | null>(null)
40
+ // In-UI, client-only prototype agents created via the "add agent" modal.
41
+ const runtimeAgents = ref<AgentArchetype[]>([])
14
42
 
15
- function get(kind: AgentKind) {
16
- return AGENT_BY_KIND[kind]
43
+ /**
44
+ * The merged CUSTOM catalog (consumer-slot → backend-manifest → runtime), each
45
+ * mapped to display metadata, de-duplicated, and never shadowing a built-in or
46
+ * system kind. The old `registerCustomKinds` only guarded `AGENT_BY_KIND`; this
47
+ * intentionally ALSO drops any custom kind colliding with a `SYSTEM_AGENT_META`
48
+ * kind (`ci` / `merger` / `blueprints` / gates …), so a snapshot can't override an
49
+ * engine kind's palette entry either — matching `agentKindMeta`'s precedence
50
+ * (built-in → system → custom), where a colliding custom kind would never win anyway.
51
+ */
52
+ const customArchetypes = computed<AgentArchetype[]>(() => {
53
+ const seen = new Set<string>()
54
+ const out: AgentArchetype[] = []
55
+ const add = (a: AgentArchetype) => {
56
+ if (a.kind in AGENT_BY_KIND || a.kind in SYSTEM_AGENT_META || seen.has(a.kind)) return
57
+ seen.add(a.kind)
58
+ out.push(a)
59
+ }
60
+ for (const k of consumerKinds.value) add(customKindToArchetype(k))
61
+ for (const k of capabilitiesManifest.value?.slots?.agentKinds ?? [])
62
+ add(customKindToArchetype(k))
63
+ for (const a of runtimeAgents.value) add(a)
64
+ return out
65
+ })
66
+
67
+ /** The full palette: built-in archetypes + the merged custom ones. */
68
+ const archetypes = computed<AgentArchetype[]>(() => [
69
+ ...AGENT_ARCHETYPES,
70
+ ...customArchetypes.value,
71
+ ])
72
+
73
+ // Known-kind lookup (built-in ∪ system ∪ custom) for `get`.
74
+ const customByKind = computed<Record<string, AgentArchetype>>(() =>
75
+ Object.fromEntries(customArchetypes.value.map((a) => [a.kind, a])),
76
+ )
77
+
78
+ // Keep `catalog.ts`'s pure-util projection in sync with the merged custom
79
+ // catalog so `agentKindMeta` / `isKnownAgentKind` resolve custom kinds. Sync
80
+ // flush so an imperative read right after `hydrateCustomKinds` (e.g. the run
81
+ // dispatch resolving a custom kind's `resultView`) sees the fresh catalog with
82
+ // no tick gap. The watch lives in the store's effect scope (disposed with it).
83
+ watch(customByKind, (map) => setCustomAgentKindMeta(map), { immediate: true, flush: 'sync' })
84
+
85
+ /** Display metadata for a KNOWN kind (built-in / system / custom), else undefined. */
86
+ function get(kind: AgentKind): AgentArchetype | undefined {
87
+ return AGENT_BY_KIND[kind] ?? SYSTEM_AGENT_META[kind] ?? customByKind.value[kind]
17
88
  }
18
89
 
90
+ /**
91
+ * Add an in-UI prototype agent (the pipeline builder's "add agent" modal).
92
+ * Client-only, so it lives in store state — no backend, no global mutation.
93
+ */
19
94
  function addAgent(input: {
20
95
  label: string
21
96
  description?: string
@@ -30,36 +105,42 @@ export const useAgentsStore = defineStore('agents', () => {
30
105
  icon: input.icon || 'i-lucide-sparkles',
31
106
  color: input.color || '#22d3ee',
32
107
  }
33
- // register for kind-based lookups across the app, then surface in the palette
34
- AGENT_BY_KIND[archetype.kind] = archetype
35
- archetypes.value.push(archetype)
108
+ runtimeAgents.value = [...runtimeAgents.value, archetype]
36
109
  return archetype
37
110
  }
38
111
 
39
112
  /**
40
- * Merge the deployment's registered CUSTOM agent kinds (from the workspace snapshot)
41
- * into the palette catalog: each becomes a first-class palette block + a kind-based
42
- * lookup (so timelines / inspectors render it instead of the generic fallback), and its
43
- * declared `resultView` opens through the same registry the built-ins use. Idempotent
44
- * and built-in-safe — a kind already known (a built-in, or a prior load) is left
45
- * untouched, so a snapshot can't shadow a built-in or duplicate on reload.
113
+ * Register the deployment's CODE-shipped consumer agent kinds the resolved
114
+ * modular `agentKinds` slot, fed once by the install plugin. Idempotent
115
+ * replace (module slots resolve once, so this is called a single time).
46
116
  */
47
- function registerCustomKinds(kinds: CustomAgentKind[]) {
48
- for (const { kind, presentation } of kinds) {
49
- if (AGENT_BY_KIND[kind]) continue
50
- const archetype: AgentArchetype = {
51
- kind,
52
- label: presentation.label,
53
- icon: presentation.icon,
54
- color: presentation.color,
55
- description: presentation.description,
56
- ...(presentation.category ? { category: presentation.category } : {}),
57
- ...(presentation.resultView ? { resultView: presentation.resultView } : {}),
58
- }
59
- AGENT_BY_KIND[kind] = archetype
60
- archetypes.value.push(archetype)
61
- }
117
+ function registerConsumerKinds(kinds: readonly CustomAgentKind[]) {
118
+ consumerKinds.value = [...kinds]
62
119
  }
63
120
 
64
- return { archetypes, get, addAgent, registerCustomKinds }
121
+ /**
122
+ * Hydrate the deployment's BACKEND-registered custom kinds from a workspace
123
+ * snapshot, modeled as a single remote capability manifest (swapped wholesale
124
+ * per workspace). Replaces the old `registerCustomKinds` that mutated
125
+ * {@link AGENT_BY_KIND} directly.
126
+ *
127
+ * The snapshot re-delivers the same deployment kinds on every board refresh, so
128
+ * skip the swap — and the downstream projection invalidation of every
129
+ * `agentKindMeta` consumer — when the content-derived manifest version is
130
+ * unchanged. A genuinely different workspace's kinds change the version and swap.
131
+ */
132
+ function hydrateCustomKinds(kinds: CustomAgentKind[]) {
133
+ const next = buildAgentCapabilitiesManifest(kinds)
134
+ if (capabilitiesManifest.value?.version === next.version) return
135
+ capabilitiesManifest.value = next
136
+ }
137
+
138
+ return {
139
+ archetypes,
140
+ customArchetypes,
141
+ get,
142
+ addAgent,
143
+ registerConsumerKinds,
144
+ hydrateCustomKinds,
145
+ }
65
146
  })
@@ -23,7 +23,7 @@ import { usePreflightsStore } from '~/stores/preflights'
23
23
  import { useServicesStore } from '~/stores/services'
24
24
  import { useWorkspaceStore } from '~/stores/workspace'
25
25
 
26
- // The environment setup wizard's cross-step state + actions (shared-stacks slice 7). It walks the
26
+ // The environment setup wizard's cross-step DATA + actions (shared-stacks slice 7). It backs the
27
27
  // guided flow — pick a service frame → review the recommended `docker-compose` recipe (detector
28
28
  // facts + the opt-in analyst draft, merged with provenance) → run the machine preflights → save
29
29
  // (persist the recipe on the frame AND register the workspace's docker-compose handler so the
@@ -31,17 +31,19 @@ import { useWorkspaceStore } from '~/stores/workspace'
31
31
  //
32
32
  // The detector + analyst only RECOMMEND; the human confirms/edits the working `recipe` here and the
33
33
  // compose provider keys purely on the saved recipe (the build-flag rule). Mirrors the other infra
34
- // stores' idiom; the flow state is a singleton so the wizard modal + its step children share it.
34
+ // stores' idiom; the flow state is a singleton so the wizard's step children share it.
35
+ //
36
+ // Since slice 3 of the modular-vue adoption (docs/initiatives/modular-vue-adoption.md) the wizard's
37
+ // step NAVIGATION lives in a modular-vue journey (`app/modular/journeys/environmentSetup.ts`), NOT
38
+ // here — this store no longer holds a `step` / `STEP_ORDER` / `goToStep`. It is purely the per-frame
39
+ // data+action layer the journey's step components drive; `beginForFrame` seeds it when a step first
40
+ // targets a frame.
35
41
 
36
42
  /** The seeded analyst-only pipeline the "run deep analysis" trigger starts against the frame. */
37
43
  const ANALYSIS_PIPELINE_ID = 'pl_environment_analysis'
38
44
  /** The analyst agent kind whose `result.custom` carries the drafted recipe. */
39
45
  const ANALYST_AGENT_KIND = 'environment-analyst'
40
46
 
41
- /** The wizard's ordered steps. `trial` is an optional post-save action, not a gate. */
42
- export type EnvWizardStep = 'pick' | 'review' | 'preflight' | 'save'
43
- export const ENV_WIZARD_STEPS: EnvWizardStep[] = ['pick', 'review', 'preflight', 'save']
44
-
45
47
  /** The analyst run's lifecycle as the wizard surfaces it. */
46
48
  export type AnalysisStatus = 'idle' | 'running' | 'ready' | 'failed'
47
49
 
@@ -70,9 +72,11 @@ export const useEnvironmentWizardStore = defineStore('environmentWizard', () =>
70
72
  const pipelines = usePipelinesStore()
71
73
  const preflights = usePreflightsStore()
72
74
 
73
- // ---- Flow position ------------------------------------------------------
75
+ // ---- Target frame -------------------------------------------------------
76
+ // The frame the flow currently targets. Set by `beginForFrame` when a journey
77
+ // step first mounts against a frame; the journey (not this store) owns which
78
+ // step is showing.
74
79
  const frameId = ref<string | null>(null)
75
- const step = ref<EnvWizardStep>('pick')
76
80
 
77
81
  // ---- Detection ----------------------------------------------------------
78
82
  const detecting = ref(false)
@@ -212,19 +216,18 @@ export const useEnvironmentWizardStore = defineStore('environmentWizard', () =>
212
216
  trialStarted.value = false
213
217
  }
214
218
 
215
- /** Reset the flow for a (possibly preselected) frame. */
216
- function open(preselectFrameId: string | null) {
217
- frameId.value = preselectFrameId
218
- step.value = preselectFrameId ? 'review' : 'pick'
219
- resetFlowState()
220
- if (preselectFrameId) void detect()
221
- }
222
-
223
- function selectFrame(id: string) {
219
+ /**
220
+ * Seed the data layer for a frame the journey's review step is entering. The
221
+ * journey owns navigation, so this is idempotent by frame: it (re)seeds +
222
+ * detects only when the target frame actually changes, so back-navigating to
223
+ * the review step (or a resume) does NOT clobber the operator's in-progress
224
+ * recipe edits. Selecting a different frame resets the flow for it.
225
+ */
226
+ function beginForFrame(id: string | null) {
227
+ if (frameId.value === id) return
224
228
  frameId.value = id
225
229
  resetFlowState()
226
- step.value = 'review'
227
- void detect()
230
+ if (id) void detect()
228
231
  }
229
232
 
230
233
  /** Re-seed the working recipe from the current merge (detector-only, or +analyst after apply). */
@@ -435,14 +438,9 @@ export const useEnvironmentWizardStore = defineStore('environmentWizard', () =>
435
438
  }
436
439
  }
437
440
 
438
- function goToStep(next: EnvWizardStep) {
439
- step.value = next
440
- }
441
-
442
441
  return {
443
442
  // state
444
443
  frameId,
445
- step,
446
444
  detecting,
447
445
  detectError,
448
446
  recommendation,
@@ -472,8 +470,7 @@ export const useEnvironmentWizardStore = defineStore('environmentWizard', () =>
472
470
  analysisStatus,
473
471
  merged,
474
472
  // actions
475
- open,
476
- selectFrame,
473
+ beginForFrame,
477
474
  detect,
478
475
  startAnalysis,
479
476
  applyAnalystDraft,
@@ -484,6 +481,5 @@ export const useEnvironmentWizardStore = defineStore('environmentWizard', () =>
484
481
  runPreflight,
485
482
  save,
486
483
  trialProvision,
487
- goToStep,
488
484
  }
489
485
  })
@@ -168,9 +168,10 @@ export const useWorkspaceStore = defineStore(
168
168
  useInitiativesStore().hydratePresets(snapshot.initiativePresets)
169
169
  useTrackerStore().hydrate(snapshot.trackerSettings)
170
170
  useServicesStore().hydrate(snapshot.mounts ?? [], snapshot.serviceCatalog ?? [])
171
- // Merge the deployment's registered custom agent kinds into the palette catalog so a
172
- // proprietary kind renders as a first-class block + result view (idempotent on reload).
173
- useAgentsStore().registerCustomKinds(snapshot.customAgentKinds ?? [])
171
+ // Hydrate the deployment's backend-registered custom agent kinds as the workspace's
172
+ // remote capability manifest, so a proprietary kind renders as a first-class palette
173
+ // block + result view. Swapped wholesale per workspace (no global-catalog mutation).
174
+ useAgentsStore().hydrateCustomKinds(snapshot.customAgentKinds ?? [])
174
175
  // The account's repo-sourced Claude Skills catalog (shared across its workspaces), so the
175
176
  // pipeline builder's per-step skill picker has its options. A straight replace.
176
177
  useSkillsStore().hydrate(snapshot.skills ?? [])
@@ -108,9 +108,10 @@ export interface AgentArchetype {
108
108
  category?: AgentCategory
109
109
  /**
110
110
  * Optional id of a DEDICATED result window this agent's step opens instead of the
111
- * generic prose step-detail panel. Resolved through the result-view registry
112
- * (`STEP_RESULT_VIEWS`) so any agent can declare a bespoke visualization without the
113
- * renderer hardcoding a kind. Absent the generic `AgentStepDetail` panel.
111
+ * generic prose step-detail panel. Resolved through the modular `resultViews` slot
112
+ * registry (`~/modular/result-views`, read by `StepResultViewHost`) so any agent
113
+ * built-in or a consumer's can declare a bespoke visualization without the renderer
114
+ * hardcoding a kind. Absent → the generic `AgentStepDetail` panel.
114
115
  */
115
116
  resultView?: string
116
117
  }
@@ -1,3 +1,4 @@
1
+ import { shallowRef } from 'vue'
1
2
  import type {
2
3
  AgentArchetype,
3
4
  AgentCategory,
@@ -304,10 +305,44 @@ export function isProducerCompanion(kind: string): boolean {
304
305
  return COMPANION_KINDS.has(kind)
305
306
  }
306
307
 
307
- export const AGENT_BY_KIND: Record<AgentKind, AgentArchetype> = Object.fromEntries(
308
- [...AGENT_ARCHETYPES, ...COMPANION_ARCHETYPES].map((a) => [a.kind, a]),
308
+ /**
309
+ * The BUILT-IN palette + companion catalog, keyed by kind. Frozen and never
310
+ * mutated: custom (deployment/consumer) kinds no longer reach into this const —
311
+ * they flow through the modular `agentKinds` slot / the workspace-capability
312
+ * remote manifest and land in {@link customAgentKindMeta} instead (slice 2 of
313
+ * the modular-vue adoption). Freezing turns any stray write into a loud runtime
314
+ * error rather than silently conflating built-ins with custom kinds.
315
+ */
316
+ export const AGENT_BY_KIND: Record<AgentKind, AgentArchetype> = Object.freeze(
317
+ Object.fromEntries([...AGENT_ARCHETYPES, ...COMPANION_ARCHETYPES].map((a) => [a.kind, a])),
309
318
  ) as Record<AgentKind, AgentArchetype>
310
319
 
320
+ /**
321
+ * Reactive read-model of the deployment's CUSTOM agent kinds (consumer-slot +
322
+ * backend remote-manifest), kept in sync by the agents store from the resolved
323
+ * modular `agentKinds` slot. The slot/manifest is the source of truth; this
324
+ * `shallowRef` is its synchronous projection so the pure kind-meta lookups below
325
+ * ({@link agentKindMeta} / {@link isKnownAgentKind}) resolve a custom kind — and
326
+ * re-render when the catalog changes — WITHOUT importing the store (which would
327
+ * be circular) or mutating {@link AGENT_BY_KIND}. Empty until the store first
328
+ * populates it, so an unknown custom kind degrades to the generic fallback,
329
+ * exactly as before registration.
330
+ */
331
+ const customAgentKindMeta = shallowRef<Record<string, AgentArchetype>>({})
332
+
333
+ /**
334
+ * Replace the custom-kind projection (called only by the agents store, whenever
335
+ * its merged custom catalog changes). Whole-map replace, not per-key mutation.
336
+ */
337
+ export function setCustomAgentKindMeta(map: Record<string, AgentArchetype>): void {
338
+ customAgentKindMeta.value = map
339
+ }
340
+
341
+ /** Test-only: clear the custom-kind projection so a spec starts from built-ins only. */
342
+ export function __resetCustomAgentKindMetaForTest(): void {
343
+ customAgentKindMeta.value = {}
344
+ }
345
+
311
346
  /**
312
347
  * Agent kinds eligible for the optional consensus mechanism (the pipeline builder shows an
313
348
  * "Enable Consensus" toggle for these). Mirrors the backend default-eligible set assigned by
@@ -596,30 +631,36 @@ const FALLBACK_AGENT_META: Omit<AgentArchetype, 'kind'> = {
596
631
  }
597
632
 
598
633
  /**
599
- * Resolve display metadata for ANY agent kind — a palette archetype (incl. custom
600
- * agents registered into {@link AGENT_BY_KIND}), an engine system kind, or an
601
- * unknown one ALWAYS returning a usable icon/label/color. This is the single
602
- * lookup every pipeline / run renderer should use so a kind missing from the
603
- * archetype map (e.g. `ci`/`merger`/`blueprints` in a seeded pipeline) can never
604
- * blow up a component with an undefined access.
634
+ * Resolve display metadata for ANY agent kind — a built-in palette archetype
635
+ * ({@link AGENT_BY_KIND}), an engine system kind ({@link SYSTEM_AGENT_META}), a
636
+ * deployment CUSTOM kind (projected from the modular `agentKinds` slot into
637
+ * {@link customAgentKindMeta} by the agents store), or an unknown one ALWAYS
638
+ * returning a usable icon/label/color. This is the single lookup every pipeline
639
+ * / run renderer should use so a kind missing from the archetype map (e.g.
640
+ * `ci`/`merger`/`blueprints` in a seeded pipeline) can never blow up a component
641
+ * with an undefined access. Reading `customAgentKindMeta` reactively means a
642
+ * component computed re-runs when the custom catalog changes.
605
643
  */
606
644
  export function agentKindMeta(kind: string): AgentArchetype {
607
645
  return (
608
646
  AGENT_BY_KIND[kind as AgentKind] ??
609
- SYSTEM_AGENT_META[kind] ?? { kind: kind as AgentKind, ...FALLBACK_AGENT_META }
647
+ SYSTEM_AGENT_META[kind] ??
648
+ customAgentKindMeta.value[kind] ?? { kind: kind as AgentKind, ...FALLBACK_AGENT_META }
610
649
  )
611
650
  }
612
651
 
613
652
  /**
614
- * Whether an agent kind is actually known to this build — a palette archetype or companion
615
- * ({@link AGENT_BY_KIND}, which deployment custom kinds are merged into via
616
- * `useAgentsStore().registerCustomKinds`), or an engine system/gate kind
617
- * ({@link SYSTEM_AGENT_META}). Unlike {@link agentKindMeta} (which always returns a usable
618
- * fallback so renderers never crash), this returns `false` for an unknown kind — used to flag
619
- * a pipeline that references a nonexistent agent. Call AFTER custom kinds are registered.
653
+ * Whether an agent kind is actually known to this build — a built-in palette
654
+ * archetype or companion ({@link AGENT_BY_KIND}), an engine system/gate kind
655
+ * ({@link SYSTEM_AGENT_META}), or a deployment CUSTOM kind projected from the
656
+ * modular `agentKinds` slot ({@link customAgentKindMeta}). Unlike
657
+ * {@link agentKindMeta} (which always returns a usable fallback so renderers
658
+ * never crash), this returns `false` for an unknown kind used to flag a
659
+ * pipeline that references a nonexistent agent. Call AFTER the workspace
660
+ * snapshot has hydrated the custom kinds.
620
661
  */
621
662
  export function isKnownAgentKind(kind: string): boolean {
622
- return kind in AGENT_BY_KIND || kind in SYSTEM_AGENT_META
663
+ return kind in AGENT_BY_KIND || kind in SYSTEM_AGENT_META || kind in customAgentKindMeta.value
623
664
  }
624
665
 
625
666
  type BlockTypeMeta = { label: string; icon: string; accent: string }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.133.0",
3
+ "version": "0.135.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",
@@ -18,14 +18,15 @@
18
18
  "access": "public"
19
19
  },
20
20
  "dependencies": {
21
- "@modular-frontend/core": "^0.1.0",
22
- "@modular-vue/core": "^1.0.1",
23
- "@modular-vue/nuxt": "^0.1.1",
24
- "@modular-vue/runtime": "^1.1.0",
25
- "@modular-vue/vue": "^1.1.0",
26
- "@nuxt/ui": "^4.9.0",
21
+ "@modular-frontend/core": "^0.2.0",
22
+ "@modular-vue/core": "^1.2.0",
23
+ "@modular-vue/journeys": "^1.1.0",
24
+ "@modular-vue/nuxt": "^0.3.0",
25
+ "@modular-vue/runtime": "^1.3.0",
26
+ "@modular-vue/vue": "^1.2.0",
27
+ "@nuxt/ui": "^4.10.0",
27
28
  "@nuxtjs/i18n": "^10.4.1",
28
- "@pinia/nuxt": "^0.11.3",
29
+ "@pinia/nuxt": "^1.0.1",
29
30
  "@toad-contracts/core": "0.4.0",
30
31
  "@toad-contracts/frontend-http-client": "0.3.2",
31
32
  "@toad-contracts/valibot": "0.5.0",
@@ -34,26 +35,26 @@
34
35
  "@vue-flow/node-resizer": "^1.5.1",
35
36
  "@vueuse/core": "^14.3.0",
36
37
  "markdown-it": "^14.3.0",
37
- "pinia": "^3.0.4",
38
+ "pinia": "^4.0.2",
38
39
  "pinia-plugin-persistedstate": "^4.7.1",
39
40
  "valibot": "^1.4.2",
40
41
  "vue": "3.5.40",
41
42
  "wretch": "^3.0.9",
42
- "@cat-factory/contracts": "0.147.1"
43
+ "@cat-factory/contracts": "0.148.0"
43
44
  },
44
45
  "devDependencies": {
45
46
  "@toad-contracts/testing": "0.3.2",
46
47
  "@types/markdown-it": "^14.1.2",
47
48
  "happy-dom": "^20.10.6",
48
49
  "msw": "^2.15.0",
49
- "nuxt": "^4.4.8",
50
+ "nuxt": "^4.5.0",
50
51
  "typescript": "^6.0.3",
51
52
  "vitest": "^4.1.10",
52
53
  "vue-i18n-extract": "^2.0.7",
53
54
  "vue-tsc": "^3.3.7"
54
55
  },
55
56
  "peerDependencies": {
56
- "nuxt": "^4.4.8"
57
+ "nuxt": "^4.5.0"
57
58
  },
58
59
  "scripts": {
59
60
  "postinstall": "nuxt prepare",