@cat-factory/app 0.85.0 → 0.86.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.
@@ -16,7 +16,7 @@ import { DOC_KINDS } from '~/types/domain'
16
16
  import ContextDocumentPicker from '~/components/documents/ContextDocumentPicker.vue'
17
17
  import ContextIssuePicker from '~/components/tasks/ContextIssuePicker.vue'
18
18
  import { mergePresetOptionLabel, mergePresetThresholds } from '~/utils/mergePreset'
19
- import { pipelineAllowedForFrame } from '~/utils/pipeline'
19
+ import { pipelineAllowedForManualStart } from '~/utils/pipeline'
20
20
 
21
21
  const ui = useUiStore()
22
22
  const board = useBoardStore()
@@ -197,9 +197,10 @@ const selectedModelPresetLabel = computed(() => {
197
197
  })
198
198
 
199
199
  // Hide UI-testing pipelines (`tester-ui` / `visual-confirmation`) when the target frame has no
200
- // UI to exercise — they'd be refused server-side (see utils/pipeline + the backend gate).
200
+ // UI to exercise — they'd be refused server-side (see utils/pipeline + the backend gate). Also
201
+ // hide `'recurring'`-only pipelines: a one-off task start of one is refused at run start.
201
202
  const selectablePipelines = computed(() =>
202
- pipelines.pipelines.filter((p) => pipelineAllowedForFrame(p, frame.value, board.blocks)),
203
+ pipelines.pipelines.filter((p) => pipelineAllowedForManualStart(p, frame.value, board.blocks)),
203
204
  )
204
205
  const pipelineMenu = computed(() => [
205
206
  [
@@ -6,7 +6,7 @@
6
6
  // pipeline is picked, the workspace issue-tracker choice is surfaced inline (it is
7
7
  // where that pipeline files its ticket) and saved alongside.
8
8
  import type { Recurrence, ScheduleTemplate } from '~/types/recurring'
9
- import { pipelineAllowedForFrame } from '~/utils/pipeline'
9
+ import { pipelineAllowedForSchedule } from '~/utils/pipeline'
10
10
 
11
11
  const ui = useUiStore()
12
12
  const board = useBoardStore()
@@ -52,8 +52,9 @@ function defaultRecurrence(): Recurrence {
52
52
  }
53
53
 
54
54
  // Hide UI-testing pipelines when the frame has no UI to exercise — they'd be refused at run start.
55
+ // Also hide `'one-off'`-only pipelines: attaching one to a schedule is refused server-side.
55
56
  const selectablePipelines = computed(() =>
56
- pipelines.pipelines.filter((p) => pipelineAllowedForFrame(p, frame.value, board.blocks)),
57
+ pipelines.pipelines.filter((p) => pipelineAllowedForSchedule(p, frame.value, board.blocks)),
57
58
  )
58
59
  const pipelineMenu = computed(() => [
59
60
  selectablePipelines.value.map((p) => ({
@@ -2,7 +2,7 @@
2
2
  import { onKeyStroke } from '@vueuse/core'
3
3
  import type { Block } from '~/types/domain'
4
4
  import { blockTypeMeta, STATUS_META } from '~/utils/catalog'
5
- import { pipelineAllowedForFrame } from '~/utils/pipeline'
5
+ import { pipelineAllowedForManualStart } from '~/utils/pipeline'
6
6
  import PipelineProgress from '~/components/pipeline/PipelineProgress.vue'
7
7
 
8
8
  const board = useBoardStore()
@@ -27,11 +27,12 @@ const deps = computed(() =>
27
27
  (block.value?.dependsOn ?? []).map((id) => board.getBlock(id)).filter((b): b is Block => !!b),
28
28
  )
29
29
 
30
- // Hide UI-testing pipelines when this block's frame has no UI to exercise (see the backend gate).
30
+ // Hide UI-testing pipelines when this block's frame has no UI to exercise, and `'recurring'`-only
31
+ // pipelines (a manual run of one is refused server-side) — see the backend gate.
31
32
  const runMenu = computed(() => {
32
33
  const frame = block.value ? board.serviceOf(block.value) : undefined
33
34
  return pipelines.pipelines
34
- .filter((p) => pipelineAllowedForFrame(p, frame, board.blocks))
35
+ .filter((p) => pipelineAllowedForManualStart(p, frame, board.blocks))
35
36
  .map((p) => ({
36
37
  label: p.name,
37
38
  icon: 'i-lucide-play',
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { Block, BlockStatus } from '~/types/domain'
3
3
  import { blockTypeMeta, STATUS_META } from '~/utils/catalog'
4
- import { pipelineAllowedForFrame } from '~/utils/pipeline'
4
+ import { pipelineAllowedForManualStart } from '~/utils/pipeline'
5
5
  import TaskContextDocs from '~/components/documents/TaskContextDocs.vue'
6
6
  import TaskContextIssues from '~/components/tasks/TaskContextIssues.vue'
7
7
  import TaskAgentConfig from '~/components/panels/inspector/TaskAgentConfig.vue'
@@ -169,12 +169,13 @@ const taskBranchUrl = computed(() => {
169
169
  return base ? `${base}/tree/${pr.branch}` : null
170
170
  })
171
171
 
172
- // Hide UI-testing pipelines when this block's frame has no UI to exercise they'd be refused
173
- // at run start (see utils/pipeline + the backend gate).
172
+ // Hide UI-testing pipelines when this block's frame has no UI to exercise, and `'recurring'`-only
173
+ // pipelines (a manual run of one is refused server-side) they'd be refused at run start (see
174
+ // utils/pipeline + the backend gate).
174
175
  const runMenu = computed(() => {
175
176
  const frame = block.value ? board.serviceOf(block.value) : undefined
176
177
  return pipelines.pipelines
177
- .filter((p) => pipelineAllowedForFrame(p, frame, board.blocks))
178
+ .filter((p) => pipelineAllowedForManualStart(p, frame, board.blocks))
178
179
  .map((p) => ({
179
180
  label: p.name,
180
181
  icon: 'i-lucide-play',
@@ -4,7 +4,7 @@ import { connectionNeighborIds } from '@cat-factory/contracts'
4
4
  import type { Block } from '~/types/domain'
5
5
  import type { WritebackOverride } from '~/types/tracker'
6
6
  import { mergePresetOptionLabel, mergePresetThresholds } from '~/utils/mergePreset'
7
- import { pipelineAllowedForFrame } from '~/utils/pipeline'
7
+ import { pipelineAllowedForManualStart } from '~/utils/pipeline'
8
8
  import InspectorSection from '~/components/panels/inspector/InspectorSection.vue'
9
9
 
10
10
  const props = defineProps<{ block: Block }>()
@@ -132,11 +132,14 @@ function setModelPreset(id: string) {
132
132
  const selectedPipeline = computed(() =>
133
133
  props.block.pipelineId ? pipelines.getPipeline(props.block.pipelineId) : undefined,
134
134
  )
135
- // Hide UI-testing pipelines when this task's frame has no UI to exercise they'd be refused at
136
- // run start (see utils/pipeline + the backend gate).
135
+ // Hide UI-testing pipelines when this task's frame has no UI to exercise, and `'recurring'`-only
136
+ // pipelines (the task's manual Run control can't start one) they'd be refused at run start
137
+ // (see utils/pipeline + the backend gate).
137
138
  const taskFrame = computed(() => board.serviceOf(props.block))
138
139
  const selectablePipelines = computed(() =>
139
- pipelines.pipelines.filter((p) => pipelineAllowedForFrame(p, taskFrame.value, board.blocks)),
140
+ pipelines.pipelines.filter((p) =>
141
+ pipelineAllowedForManualStart(p, taskFrame.value, board.blocks),
142
+ ),
140
143
  )
141
144
  const pipelineMenu = computed(() => [
142
145
  [
@@ -20,3 +20,33 @@ export function pipelineAllowedForFrame(
20
20
  ): boolean {
21
21
  return !pipelineHasVisualStep(pipeline) || frameAllowsVisualPipeline(frame, blocks)
22
22
  }
23
+
24
+ // Launch-availability filters, the surface counterpart to the backend's start-origin gate (a
25
+ // `'recurring'`-only pipeline can't be started as a one-off manual task, and a `'one-off'`-only
26
+ // pipeline can't be attached to a schedule). `availability` absent ⇒ `'both'` (unrestricted), so
27
+ // legacy/unset pipelines pass both. Composed with {@link pipelineAllowedForFrame} at each picker.
28
+
29
+ /**
30
+ * Whether `pipeline` may be started as a MANUAL one-off task run (the board/inspector Run menus,
31
+ * the add-task modal, the task run-settings default). Excludes `'recurring'`-only pipelines the
32
+ * backend would refuse.
33
+ */
34
+ export function pipelineAllowedForManualStart(
35
+ pipeline: Pipeline,
36
+ frame: Block | undefined,
37
+ blocks: readonly Block[],
38
+ ): boolean {
39
+ return pipeline.availability !== 'recurring' && pipelineAllowedForFrame(pipeline, frame, blocks)
40
+ }
41
+
42
+ /**
43
+ * Whether `pipeline` may be attached to a RECURRING schedule (the recurring-pipeline modal).
44
+ * Excludes `'one-off'`-only pipelines the backend would refuse.
45
+ */
46
+ export function pipelineAllowedForSchedule(
47
+ pipeline: Pipeline,
48
+ frame: Block | undefined,
49
+ blocks: readonly Block[],
50
+ ): boolean {
51
+ return pipeline.availability !== 'one-off' && pipelineAllowedForFrame(pipeline, frame, blocks)
52
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cat-factory/app",
3
- "version": "0.85.0",
3
+ "version": "0.86.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",
@@ -34,7 +34,7 @@
34
34
  "valibot": "^1.4.2",
35
35
  "vue": "^3.5.39",
36
36
  "wretch": "^3.0.9",
37
- "@cat-factory/contracts": "0.92.0"
37
+ "@cat-factory/contracts": "0.93.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@toad-contracts/testing": "0.3.2",