@cat-factory/app 0.194.0 → 0.195.1
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/initiative/InitiativePlanDecision.vue +134 -0
- package/app/components/initiative/InitiativePlanNotice.vue +69 -0
- package/app/components/initiative/InitiativePlanReview.vue +278 -254
- package/app/components/initiative/InitiativeTrackerWindow.vue +72 -25
- package/app/components/pipeline/AgentPromptEditor.vue +40 -0
- package/app/components/pipeline/OutputBudgetInput.vue +69 -0
- package/app/components/pipeline/PipelineBuilder.vue +42 -0
- package/app/composables/api/agentSettings.ts +32 -0
- package/app/composables/useApi.ts +2 -0
- package/app/stores/agentSettings.ts +86 -0
- package/app/stores/outputBudget.spec.ts +55 -0
- package/app/stores/pipelines/draftStepConfig.ts +23 -0
- package/app/types/agent-settings.ts +12 -0
- package/app/utils/initiative.spec.ts +43 -0
- package/app/utils/initiative.ts +23 -0
- package/i18n/locales/de.json +12 -3
- package/i18n/locales/en.json +12 -3
- package/i18n/locales/es.json +12 -3
- package/i18n/locales/fr.json +12 -3
- package/i18n/locales/he.json +12 -3
- package/i18n/locales/it.json +12 -3
- package/i18n/locales/ja.json +12 -3
- package/i18n/locales/pl.json +12 -3
- package/i18n/locales/tr.json +12 -3
- package/i18n/locales/uk.json +12 -3
- package/package.json +2 -2
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The plan gate's DECISION half: the overall-feedback field plus the two commands that resolve the
|
|
3
|
+
// park — accept the plan, or send it back to the planner.
|
|
4
|
+
//
|
|
5
|
+
// It is its own component because the gate has two surfaces: the full document review
|
|
6
|
+
// (`InitiativePlanReview`, which owns the tracker window while a rendered plan is parked) and the
|
|
7
|
+
// compact notice a gate with no rendered plan falls back to (`InitiativePlanNotice`). Both must
|
|
8
|
+
// word the commands identically, gate them on the same RBAC fact, and refuse an empty send-back
|
|
9
|
+
// the same way — so the state machine and its markup live here once rather than in each.
|
|
10
|
+
//
|
|
11
|
+
// Deliberately NOT offered: "approve with corrections". The plan was ingested into the
|
|
12
|
+
// `initiatives` entity before this gate was raised, so the document is a VIEW of committed state —
|
|
13
|
+
// an edit typed over it would reach nothing, and the engine refuses it outright
|
|
14
|
+
// (`outputIsRendered` → 422). Requesting changes is the route for a correction, which is why an
|
|
15
|
+
// anchored comment is worth having: it quotes the planner's own text back to it on the re-plan.
|
|
16
|
+
import { computed, ref, watch } from 'vue'
|
|
17
|
+
import type { RequestStepChangesInput } from '@cat-factory/contracts'
|
|
18
|
+
|
|
19
|
+
const props = defineProps<{
|
|
20
|
+
/** The parked gate being resolved. */
|
|
21
|
+
approvalId: string
|
|
22
|
+
/** The run the gate belongs to, for the approve / request-changes commands. */
|
|
23
|
+
instanceId: string
|
|
24
|
+
/** Whether the viewer may resolve runs at all (RBAC); false renders the actions disabled. */
|
|
25
|
+
canExecute: boolean
|
|
26
|
+
/**
|
|
27
|
+
* The anchored per-block comments to send with a send-back, from the surface that offers
|
|
28
|
+
* commenting. Absent on the notice surface, which has no document to anchor to.
|
|
29
|
+
*/
|
|
30
|
+
comments?: RequestStepChangesInput['comments']
|
|
31
|
+
}>()
|
|
32
|
+
|
|
33
|
+
/** A send-back succeeded: the surface drops its anchored drafts (the feedback is cleared here). */
|
|
34
|
+
const emit = defineEmits<{ sent: [] }>()
|
|
35
|
+
|
|
36
|
+
const execution = useExecutionStore()
|
|
37
|
+
const { t } = useI18n()
|
|
38
|
+
|
|
39
|
+
const feedback = ref('')
|
|
40
|
+
const submitting = ref(false)
|
|
41
|
+
|
|
42
|
+
/** Changes can only be requested with something to act on — an empty send would re-plan blind. */
|
|
43
|
+
const canRequestChanges = computed(
|
|
44
|
+
() => !!feedback.value.trim() || (props.comments?.length ?? 0) > 0,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
/** A fresh gate (a re-plan parked again) reviews the new plan clean, drafts dropped. */
|
|
48
|
+
watch(
|
|
49
|
+
() => props.approvalId,
|
|
50
|
+
() => {
|
|
51
|
+
feedback.value = ''
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Accept the plan: the run advances to the committer, which persists it and arms the execution
|
|
57
|
+
* loop. The window stays open — the review disappears with the approval (live) and the tracker it
|
|
58
|
+
* gives the window back to is where the plan then executes.
|
|
59
|
+
*/
|
|
60
|
+
async function approve() {
|
|
61
|
+
if (submitting.value || !props.canExecute) return
|
|
62
|
+
submitting.value = true
|
|
63
|
+
try {
|
|
64
|
+
await execution.approveStep(props.instanceId, props.approvalId)
|
|
65
|
+
} finally {
|
|
66
|
+
submitting.value = false
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Send the plan back: the planner re-plans from the feedback + the anchored comments. */
|
|
71
|
+
async function requestChanges() {
|
|
72
|
+
if (submitting.value || !canRequestChanges.value || !props.canExecute) return
|
|
73
|
+
submitting.value = true
|
|
74
|
+
try {
|
|
75
|
+
const ok = await execution.requestStepChanges(props.instanceId, props.approvalId, {
|
|
76
|
+
feedback: feedback.value.trim() || undefined,
|
|
77
|
+
comments: props.comments,
|
|
78
|
+
})
|
|
79
|
+
if (ok) {
|
|
80
|
+
feedback.value = ''
|
|
81
|
+
emit('sent')
|
|
82
|
+
}
|
|
83
|
+
} finally {
|
|
84
|
+
submitting.value = false
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const disabledTitle = computed(() => (props.canExecute ? undefined : t('access.noRunExecute')))
|
|
89
|
+
</script>
|
|
90
|
+
|
|
91
|
+
<template>
|
|
92
|
+
<div>
|
|
93
|
+
<UTextarea
|
|
94
|
+
v-model="feedback"
|
|
95
|
+
data-testid="initiative-plan-feedback"
|
|
96
|
+
:rows="2"
|
|
97
|
+
autoresize
|
|
98
|
+
size="sm"
|
|
99
|
+
class="w-full"
|
|
100
|
+
:placeholder="t('initiative.planReview.feedbackPlaceholder')"
|
|
101
|
+
/>
|
|
102
|
+
<div class="mt-2 flex flex-wrap items-center gap-2">
|
|
103
|
+
<UButton
|
|
104
|
+
color="primary"
|
|
105
|
+
size="xs"
|
|
106
|
+
icon="i-lucide-check"
|
|
107
|
+
data-testid="initiative-plan-approve"
|
|
108
|
+
:loading="submitting"
|
|
109
|
+
:disabled="!canExecute"
|
|
110
|
+
:title="disabledTitle"
|
|
111
|
+
@click="approve"
|
|
112
|
+
>
|
|
113
|
+
{{ t('initiative.planReview.approve') }}
|
|
114
|
+
</UButton>
|
|
115
|
+
<UButton
|
|
116
|
+
color="warning"
|
|
117
|
+
variant="soft"
|
|
118
|
+
size="xs"
|
|
119
|
+
icon="i-lucide-rotate-ccw"
|
|
120
|
+
data-testid="initiative-plan-send-back"
|
|
121
|
+
:loading="submitting"
|
|
122
|
+
:disabled="!canRequestChanges || !canExecute"
|
|
123
|
+
:title="
|
|
124
|
+
canExecute && !canRequestChanges
|
|
125
|
+
? t('initiative.planReview.needsFeedback')
|
|
126
|
+
: disabledTitle
|
|
127
|
+
"
|
|
128
|
+
@click="requestChanges"
|
|
129
|
+
>
|
|
130
|
+
{{ t('initiative.planReview.sendBack') }}
|
|
131
|
+
</UButton>
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
</template>
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// The plan gate with NO document to review: the compact rail that sits above the tracker's own
|
|
3
|
+
// sections, states that there is nothing to navigate, and still resolves the park.
|
|
4
|
+
//
|
|
5
|
+
// It is NOT a legacy shim for gates parked before the engine rendered plans. `outputIsRendered` is a
|
|
6
|
+
// fact about the STEP, and a plan gate can park without it today: the planner's post-completion
|
|
7
|
+
// resolver authors the rendering only once it has INGESTED the plan, so a run that reaches the gate
|
|
8
|
+
// without an ingest parks on the planner's transcript summary instead — a perfectly non-empty string
|
|
9
|
+
// that `planReviewDocument` correctly reads as no document (dressing one sentence up under a table
|
|
10
|
+
// of contents is the failure the whole review surface exists to end). Without this surface such a
|
|
11
|
+
// gate would have no resolving surface at all, which is the exact bug the plan-review e2e spec was
|
|
12
|
+
// written for.
|
|
13
|
+
//
|
|
14
|
+
// So it is deliberately NOT a takeover: unlike `InitiativePlanReview`, which replaces the tracker
|
|
15
|
+
// because the document repeats it, this one wants the tracker underneath — there the sections ARE
|
|
16
|
+
// the only rendering of the plan there is. Which is why it has to be TOLD whether they are actually
|
|
17
|
+
// on screen: it renders above the entity branch, because a gate can be parked while the entity is
|
|
18
|
+
// still loading, and pointing a reviewer at sections the window is not showing is worse than saying
|
|
19
|
+
// nothing.
|
|
20
|
+
import type { StepApproval } from '~/types/execution'
|
|
21
|
+
import InitiativePlanDecision from '~/components/initiative/InitiativePlanDecision.vue'
|
|
22
|
+
|
|
23
|
+
defineProps<{
|
|
24
|
+
/** The parked gate. */
|
|
25
|
+
approval: StepApproval
|
|
26
|
+
/** The run the gate belongs to, for the approve / request-changes commands. */
|
|
27
|
+
instanceId: string
|
|
28
|
+
/** Whether the viewer may resolve runs at all (RBAC); false renders the actions disabled. */
|
|
29
|
+
canExecute: boolean
|
|
30
|
+
/**
|
|
31
|
+
* Whether the tracker's own goal / phases / policy sections are rendered below this notice — i.e.
|
|
32
|
+
* whether the initiative entity has loaded. Only then may the notice point at them as the plan.
|
|
33
|
+
*/
|
|
34
|
+
hasSections: boolean
|
|
35
|
+
}>()
|
|
36
|
+
|
|
37
|
+
const { t } = useI18n()
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<section
|
|
42
|
+
class="mb-4 rounded-lg border border-amber-500/40 bg-amber-500/10 p-3.5"
|
|
43
|
+
data-testid="initiative-plan-notice"
|
|
44
|
+
>
|
|
45
|
+
<header class="flex items-start gap-2.5">
|
|
46
|
+
<UIcon name="i-lucide-clipboard-check" class="mt-0.5 h-4 w-4 shrink-0 text-amber-300" />
|
|
47
|
+
<div class="min-w-0 flex-1">
|
|
48
|
+
<h3 class="text-[13px] font-semibold text-amber-200">
|
|
49
|
+
{{ t('initiative.planReview.title') }}
|
|
50
|
+
</h3>
|
|
51
|
+
<p class="mt-0.5 text-[12px] leading-relaxed text-amber-100/80">
|
|
52
|
+
{{ t('initiative.planReview.body') }}
|
|
53
|
+
</p>
|
|
54
|
+
<p class="mt-2 text-[12px] leading-relaxed text-amber-100/70">
|
|
55
|
+
{{ t('initiative.planReview.noDocument') }}
|
|
56
|
+
<template v-if="hasSections">
|
|
57
|
+
{{ t('initiative.planReview.noDocumentSections') }}
|
|
58
|
+
</template>
|
|
59
|
+
</p>
|
|
60
|
+
</div>
|
|
61
|
+
</header>
|
|
62
|
+
<InitiativePlanDecision
|
|
63
|
+
class="mt-3"
|
|
64
|
+
:approval-id="approval.id"
|
|
65
|
+
:instance-id="instanceId"
|
|
66
|
+
:can-execute="canExecute"
|
|
67
|
+
/>
|
|
68
|
+
</section>
|
|
69
|
+
</template>
|