@gitlab/duo-ui 8.21.0 → 8.22.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/CHANGELOG.md +14 -0
- package/dist/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_flow.js +133 -0
- package/dist/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_modal/agentic_tool_approval_modal.js +137 -0
- package/dist/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_rejection_modal/agentic_tool_rejection_modal.js +128 -0
- package/dist/components/chat/components/duo_chat_threads/duo_chat_threads.js +5 -4
- package/dist/index.js +1 -0
- package/package.json +4 -4
- package/src/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_flow.md +18 -0
- package/src/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_flow.vue +119 -0
- package/src/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_modal/agentic_tool_approval_modal.vue +138 -0
- package/src/components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_rejection_modal/agentic_tool_rejection_modal.vue +134 -0
- package/src/components/chat/components/duo_chat_threads/duo_chat_threads.vue +5 -12
- package/src/index.js +1 -0
- package/translations.js +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [8.22.1](https://gitlab.com/gitlab-org/duo-ui/compare/v8.22.0...v8.22.1) (2025-06-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* **chat:** Fix chat history group dates ([548c507](https://gitlab.com/gitlab-org/duo-ui/commit/548c5078090d0a648824727a59a0fe6f727d31f2))
|
|
7
|
+
|
|
8
|
+
# [8.22.0](https://gitlab.com/gitlab-org/duo-ui/compare/v8.21.0...v8.22.0) (2025-06-18)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* **agentic-chat:** add tool approval flow component ([60630a0](https://gitlab.com/gitlab-org/duo-ui/commit/60630a09dd2f41e0eb4c8d9c9d45ae130f9e996f))
|
|
14
|
+
|
|
1
15
|
# [8.21.0](https://gitlab.com/gitlab-org/duo-ui/compare/v8.20.0...v8.21.0) (2025-06-09)
|
|
2
16
|
|
|
3
17
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { translate } from '../../../../utils/i18n';
|
|
2
|
+
import AgenticToolApprovalModal from './agentic_tool_approval_modal/agentic_tool_approval_modal';
|
|
3
|
+
import AgenticToolRejectionModal from './agentic_tool_rejection_modal/agentic_tool_rejection_modal';
|
|
4
|
+
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
5
|
+
|
|
6
|
+
const i18n = {
|
|
7
|
+
DEFAULT_DESCRIPTION: translate('AgenticToolApprovalFlow.defaultDescription', 'Duo Workflow wants to execute a tool. Do you want to proceed?')
|
|
8
|
+
};
|
|
9
|
+
const MODAL_TYPES = {
|
|
10
|
+
APPROVAL: 'approval',
|
|
11
|
+
REJECTION: 'rejection'
|
|
12
|
+
};
|
|
13
|
+
var script = {
|
|
14
|
+
name: 'AgenticToolApprovalFlow',
|
|
15
|
+
components: {
|
|
16
|
+
AgenticToolApprovalModal,
|
|
17
|
+
AgenticToolRejectionModal
|
|
18
|
+
},
|
|
19
|
+
props: {
|
|
20
|
+
/**
|
|
21
|
+
* Controls the visibility of the approval flow
|
|
22
|
+
*/
|
|
23
|
+
visible: {
|
|
24
|
+
type: Boolean,
|
|
25
|
+
required: true
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* Details about the tool to be executed
|
|
29
|
+
* @property {string} name - The name of the tool
|
|
30
|
+
* @property {Object} parameters - The parameters to be passed to the tool
|
|
31
|
+
*/
|
|
32
|
+
toolDetails: {
|
|
33
|
+
type: Object,
|
|
34
|
+
required: false,
|
|
35
|
+
default: () => ({}),
|
|
36
|
+
validator: value => {
|
|
37
|
+
return typeof value === 'object' && value !== null;
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* Custom description text for the approval request
|
|
42
|
+
*/
|
|
43
|
+
description: {
|
|
44
|
+
type: String,
|
|
45
|
+
required: false,
|
|
46
|
+
default: i18n.DEFAULT_DESCRIPTION
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
data() {
|
|
50
|
+
return {
|
|
51
|
+
currentModal: MODAL_TYPES.APPROVAL
|
|
52
|
+
};
|
|
53
|
+
},
|
|
54
|
+
computed: {
|
|
55
|
+
showApprovalModal() {
|
|
56
|
+
return this.visible && this.currentModal === MODAL_TYPES.APPROVAL;
|
|
57
|
+
},
|
|
58
|
+
showRejectionModal() {
|
|
59
|
+
return this.visible && this.currentModal === MODAL_TYPES.REJECTION;
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
watch: {
|
|
63
|
+
visible(newVal) {
|
|
64
|
+
if (!newVal) {
|
|
65
|
+
this.currentModal = MODAL_TYPES.APPROVAL;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
methods: {
|
|
70
|
+
handleApprove() {
|
|
71
|
+
/**
|
|
72
|
+
* Emitted when the user approves the tool execution
|
|
73
|
+
*/
|
|
74
|
+
this.$emit('approve');
|
|
75
|
+
},
|
|
76
|
+
handleInitialDeny() {
|
|
77
|
+
this.currentModal = MODAL_TYPES.REJECTION;
|
|
78
|
+
},
|
|
79
|
+
handleForceDeny() {
|
|
80
|
+
this.$emit('deny', null);
|
|
81
|
+
},
|
|
82
|
+
handleRejectionSubmit(message) {
|
|
83
|
+
/**
|
|
84
|
+
* Emitted when the user denies the tool execution with an optional reason
|
|
85
|
+
* @param {string|null} reason - The rejection reason provided by the user
|
|
86
|
+
*/
|
|
87
|
+
this.$emit('deny', message);
|
|
88
|
+
},
|
|
89
|
+
handleBack() {
|
|
90
|
+
this.currentModal = MODAL_TYPES.APPROVAL;
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
i18n
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/* script */
|
|
97
|
+
const __vue_script__ = script;
|
|
98
|
+
|
|
99
|
+
/* template */
|
|
100
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('div',{attrs:{"data-testid":"agentic-tool-approval-flow"}},[_c('agentic-tool-approval-modal',{attrs:{"visible":_vm.showApprovalModal,"tool-details":_vm.toolDetails,"description":_vm.description},on:{"approve":_vm.handleApprove,"deny":_vm.handleInitialDeny,"deny-force":_vm.handleForceDeny}}),_vm._v(" "),_c('agentic-tool-rejection-modal',{attrs:{"visible":_vm.showRejectionModal},on:{"submit":_vm.handleRejectionSubmit,"back":_vm.handleBack}})],1)};
|
|
101
|
+
var __vue_staticRenderFns__ = [];
|
|
102
|
+
|
|
103
|
+
/* style */
|
|
104
|
+
const __vue_inject_styles__ = undefined;
|
|
105
|
+
/* scoped */
|
|
106
|
+
const __vue_scope_id__ = undefined;
|
|
107
|
+
/* module identifier */
|
|
108
|
+
const __vue_module_identifier__ = undefined;
|
|
109
|
+
/* functional template */
|
|
110
|
+
const __vue_is_functional_template__ = false;
|
|
111
|
+
/* style inject */
|
|
112
|
+
|
|
113
|
+
/* style inject SSR */
|
|
114
|
+
|
|
115
|
+
/* style inject shadow dom */
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
const __vue_component__ = __vue_normalize__(
|
|
120
|
+
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
|
121
|
+
__vue_inject_styles__,
|
|
122
|
+
__vue_script__,
|
|
123
|
+
__vue_scope_id__,
|
|
124
|
+
__vue_is_functional_template__,
|
|
125
|
+
__vue_module_identifier__,
|
|
126
|
+
false,
|
|
127
|
+
undefined,
|
|
128
|
+
undefined,
|
|
129
|
+
undefined
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
export default __vue_component__;
|
|
133
|
+
export { i18n };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { GlModal } from '@gitlab/ui';
|
|
2
|
+
import { translate } from '../../../../../utils/i18n';
|
|
3
|
+
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
4
|
+
|
|
5
|
+
const i18n = {
|
|
6
|
+
TITLE: translate('AgenticToolApprovalModal.title', 'Tool Approval Required'),
|
|
7
|
+
DEFAULT_DESCRIPTION: translate('AgenticToolApprovalModal.defaultDescription', 'Duo Workflow wants to execute a tool. Do you want to proceed?'),
|
|
8
|
+
TOOL_LABEL: translate('AgenticToolApprovalModal.toolLabel', 'Tool:'),
|
|
9
|
+
TOOL_UNKNOWN: translate('AgenticToolApprovalModal.toolUnknown', 'Unknown'),
|
|
10
|
+
APPROVE_TEXT: translate('AgenticToolApprovalModal.approveText', 'Approve'),
|
|
11
|
+
DENY_TEXT: translate('AgenticToolApprovalModal.denyText', 'Deny')
|
|
12
|
+
};
|
|
13
|
+
var script = {
|
|
14
|
+
name: 'AgenticToolApprovalModal',
|
|
15
|
+
components: {
|
|
16
|
+
GlModal
|
|
17
|
+
},
|
|
18
|
+
props: {
|
|
19
|
+
/**
|
|
20
|
+
* Controls the visibility of the modal
|
|
21
|
+
*/
|
|
22
|
+
visible: {
|
|
23
|
+
type: Boolean,
|
|
24
|
+
required: true
|
|
25
|
+
},
|
|
26
|
+
/**
|
|
27
|
+
* Details about the tool to be executed
|
|
28
|
+
* @property {string} name - The name of the tool
|
|
29
|
+
* @property {Object} parameters - The parameters to be passed to the tool
|
|
30
|
+
*/
|
|
31
|
+
toolDetails: {
|
|
32
|
+
type: Object,
|
|
33
|
+
required: false,
|
|
34
|
+
default: () => ({}),
|
|
35
|
+
validator: value => {
|
|
36
|
+
return typeof value === 'object' && value !== null;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
/**
|
|
40
|
+
* Custom description text for the approval request
|
|
41
|
+
*/
|
|
42
|
+
description: {
|
|
43
|
+
type: String,
|
|
44
|
+
required: false,
|
|
45
|
+
default: i18n.DEFAULT_DESCRIPTION
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
computed: {
|
|
49
|
+
approveAction() {
|
|
50
|
+
return {
|
|
51
|
+
text: this.$options.i18n.APPROVE_TEXT,
|
|
52
|
+
attributes: {
|
|
53
|
+
variant: 'confirm',
|
|
54
|
+
icon: 'play',
|
|
55
|
+
size: 'small',
|
|
56
|
+
'data-testid': 'approve-tool'
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
denyAction() {
|
|
61
|
+
return {
|
|
62
|
+
text: this.$options.i18n.DENY_TEXT,
|
|
63
|
+
attributes: {
|
|
64
|
+
variant: 'danger',
|
|
65
|
+
icon: 'cancel',
|
|
66
|
+
size: 'small',
|
|
67
|
+
'data-testid': 'deny-tool'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
toolName() {
|
|
72
|
+
var _this$toolDetails;
|
|
73
|
+
return ((_this$toolDetails = this.toolDetails) === null || _this$toolDetails === void 0 ? void 0 : _this$toolDetails.name) || this.$options.i18n.TOOL_UNKNOWN;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
methods: {
|
|
77
|
+
handleApprove() {
|
|
78
|
+
/**
|
|
79
|
+
* Emitted when the user approves the tool execution
|
|
80
|
+
*/
|
|
81
|
+
this.$emit('approve');
|
|
82
|
+
},
|
|
83
|
+
handleDeny() {
|
|
84
|
+
/**
|
|
85
|
+
* Emitted when the user denies the tool execution
|
|
86
|
+
*/
|
|
87
|
+
this.$emit('deny');
|
|
88
|
+
},
|
|
89
|
+
handleModalHide(event) {
|
|
90
|
+
// If the modal is being hidden by the X button or ESC key,
|
|
91
|
+
// treat it as a deny-force action
|
|
92
|
+
if (event.trigger !== 'ok' && event.trigger !== 'cancel') {
|
|
93
|
+
this.$emit('deny-force');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
i18n
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/* script */
|
|
101
|
+
const __vue_script__ = script;
|
|
102
|
+
|
|
103
|
+
/* template */
|
|
104
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-modal',{attrs:{"visible":_vm.visible,"modal-id":"agentic-tool-approval-modal","title":_vm.$options.i18n.TITLE,"action-primary":_vm.approveAction,"action-cancel":_vm.denyAction,"no-close-on-backdrop":true,"no-close-on-esc":true,"data-testid":"agentic-tool-approval-modal"},on:{"primary":_vm.handleApprove,"canceled":_vm.handleDeny,"hide":_vm.handleModalHide}},[_c('div',{staticClass:"gl-mb-4"},[_c('p',{staticClass:"gl-mb-3",attrs:{"data-testid":"approval-description"}},[_vm._v("\n "+_vm._s(_vm.description)+"\n ")]),_vm._v(" "),(_vm.toolDetails)?_c('div',{staticClass:"gl-bg-gray-10 gl-p-3 gl-rounded-base gl-mb-3",attrs:{"data-testid":"tool-details"}},[_c('strong',[_vm._v(_vm._s(_vm.$options.i18n.TOOL_LABEL)+" "+_vm._s(_vm.toolName))]),_vm._v(" "),(_vm.toolDetails.parameters)?_c('pre',{staticClass:"gl-mt-2 gl-mb-0 gl-text-inherit",attrs:{"data-testid":"tool-parameters"}},[_vm._v(_vm._s(JSON.stringify(_vm.toolDetails.parameters, null, 2)))]):_vm._e()]):_vm._e()])])};
|
|
105
|
+
var __vue_staticRenderFns__ = [];
|
|
106
|
+
|
|
107
|
+
/* style */
|
|
108
|
+
const __vue_inject_styles__ = undefined;
|
|
109
|
+
/* scoped */
|
|
110
|
+
const __vue_scope_id__ = undefined;
|
|
111
|
+
/* module identifier */
|
|
112
|
+
const __vue_module_identifier__ = undefined;
|
|
113
|
+
/* functional template */
|
|
114
|
+
const __vue_is_functional_template__ = false;
|
|
115
|
+
/* style inject */
|
|
116
|
+
|
|
117
|
+
/* style inject SSR */
|
|
118
|
+
|
|
119
|
+
/* style inject shadow dom */
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
const __vue_component__ = __vue_normalize__(
|
|
124
|
+
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
|
125
|
+
__vue_inject_styles__,
|
|
126
|
+
__vue_script__,
|
|
127
|
+
__vue_scope_id__,
|
|
128
|
+
__vue_is_functional_template__,
|
|
129
|
+
__vue_module_identifier__,
|
|
130
|
+
false,
|
|
131
|
+
undefined,
|
|
132
|
+
undefined,
|
|
133
|
+
undefined
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
export default __vue_component__;
|
|
137
|
+
export { i18n };
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { GlModal, GlFormTextarea, GlFormGroup } from '@gitlab/ui';
|
|
2
|
+
import { translate } from '../../../../../utils/i18n';
|
|
3
|
+
import __vue_normalize__ from 'vue-runtime-helpers/dist/normalize-component.js';
|
|
4
|
+
|
|
5
|
+
const i18n = {
|
|
6
|
+
TITLE: translate('AgenticToolRejectionModal.title', 'Provide Rejection Reason'),
|
|
7
|
+
DESCRIPTION: translate('AgenticToolRejectionModal.description', 'You are about to reject the tool execution. Would you like to provide a reason?'),
|
|
8
|
+
LABEL: translate('AgenticToolRejectionModal.label', 'Rejection reason'),
|
|
9
|
+
PLACEHOLDER: translate('AgenticToolRejectionModal.placeholder', "Explain why you're rejecting this tool execution..."),
|
|
10
|
+
SUBMIT_TEXT: translate('AgenticToolRejectionModal.submitText', 'Submit Rejection'),
|
|
11
|
+
BACK_TEXT: translate('AgenticToolRejectionModal.backText', 'Back')
|
|
12
|
+
};
|
|
13
|
+
var script = {
|
|
14
|
+
name: 'AgenticToolRejectionModal',
|
|
15
|
+
components: {
|
|
16
|
+
GlModal,
|
|
17
|
+
GlFormTextarea,
|
|
18
|
+
GlFormGroup
|
|
19
|
+
},
|
|
20
|
+
props: {
|
|
21
|
+
/**
|
|
22
|
+
* Controls the visibility of the modal
|
|
23
|
+
*/
|
|
24
|
+
visible: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
required: true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
data() {
|
|
30
|
+
return {
|
|
31
|
+
rejectionMessage: ''
|
|
32
|
+
};
|
|
33
|
+
},
|
|
34
|
+
computed: {
|
|
35
|
+
submitAction() {
|
|
36
|
+
return {
|
|
37
|
+
text: this.$options.i18n.SUBMIT_TEXT,
|
|
38
|
+
attributes: {
|
|
39
|
+
variant: 'danger',
|
|
40
|
+
icon: 'cancel',
|
|
41
|
+
size: 'small',
|
|
42
|
+
'data-testid': 'submit-rejection'
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
},
|
|
46
|
+
backAction() {
|
|
47
|
+
return {
|
|
48
|
+
text: this.$options.i18n.BACK_TEXT,
|
|
49
|
+
attributes: {
|
|
50
|
+
variant: 'default',
|
|
51
|
+
icon: 'chevron-left',
|
|
52
|
+
size: 'small',
|
|
53
|
+
'data-testid': 'back-button'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
watch: {
|
|
59
|
+
visible(newVal) {
|
|
60
|
+
if (!newVal) {
|
|
61
|
+
this.rejectionMessage = '';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
methods: {
|
|
66
|
+
handleSubmit() {
|
|
67
|
+
/**
|
|
68
|
+
* Emitted when the user submits the rejection reason
|
|
69
|
+
* @param {string|null} reason - The rejection reason provided by the user, or null if empty
|
|
70
|
+
*/
|
|
71
|
+
this.$emit('submit', this.rejectionMessage || null);
|
|
72
|
+
this.rejectionMessage = '';
|
|
73
|
+
},
|
|
74
|
+
handleBack() {
|
|
75
|
+
/**
|
|
76
|
+
* Emitted when the user clicks the back button to return to the approval modal
|
|
77
|
+
*/
|
|
78
|
+
this.$emit('back');
|
|
79
|
+
},
|
|
80
|
+
handleModalHide(event) {
|
|
81
|
+
// If the modal is being hidden by the X button or ESC key,
|
|
82
|
+
// treat it as a deny-force action
|
|
83
|
+
if (event.trigger !== 'ok' && event.trigger !== 'cancel') {
|
|
84
|
+
this.handleBack();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
i18n
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
/* script */
|
|
92
|
+
const __vue_script__ = script;
|
|
93
|
+
|
|
94
|
+
/* template */
|
|
95
|
+
var __vue_render__ = function () {var _vm=this;var _h=_vm.$createElement;var _c=_vm._self._c||_h;return _c('gl-modal',{attrs:{"visible":_vm.visible,"modal-id":"agentic-tool-rejection-modal","title":_vm.$options.i18n.TITLE,"action-primary":_vm.submitAction,"action-secondary":_vm.backAction,"no-close-on-backdrop":true,"no-close-on-esc":true,"data-testid":"agentic-tool-rejection-modal"},on:{"primary":_vm.handleSubmit,"secondary":_vm.handleBack,"hide":_vm.handleModalHide}},[_c('p',{staticClass:"gl-mb-3",attrs:{"data-testid":"rejection-description"}},[_vm._v("\n "+_vm._s(_vm.$options.i18n.DESCRIPTION)+"\n ")]),_vm._v(" "),_c('gl-form-group',{staticClass:"gl-mb-0",attrs:{"label":_vm.$options.i18n.LABEL,"label-for":"rejection-message","optional":true}},[_c('gl-form-textarea',{attrs:{"id":"rejection-message","placeholder":_vm.$options.i18n.PLACEHOLDER,"rows":3,"max-rows":6,"resize":false,"data-testid":"rejection-message-input","autofocus":""},model:{value:(_vm.rejectionMessage),callback:function ($$v) {_vm.rejectionMessage=$$v;},expression:"rejectionMessage"}})],1)],1)};
|
|
96
|
+
var __vue_staticRenderFns__ = [];
|
|
97
|
+
|
|
98
|
+
/* style */
|
|
99
|
+
const __vue_inject_styles__ = undefined;
|
|
100
|
+
/* scoped */
|
|
101
|
+
const __vue_scope_id__ = undefined;
|
|
102
|
+
/* module identifier */
|
|
103
|
+
const __vue_module_identifier__ = undefined;
|
|
104
|
+
/* functional template */
|
|
105
|
+
const __vue_is_functional_template__ = false;
|
|
106
|
+
/* style inject */
|
|
107
|
+
|
|
108
|
+
/* style inject SSR */
|
|
109
|
+
|
|
110
|
+
/* style inject shadow dom */
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
const __vue_component__ = __vue_normalize__(
|
|
115
|
+
{ render: __vue_render__, staticRenderFns: __vue_staticRenderFns__ },
|
|
116
|
+
__vue_inject_styles__,
|
|
117
|
+
__vue_script__,
|
|
118
|
+
__vue_scope_id__,
|
|
119
|
+
__vue_is_functional_template__,
|
|
120
|
+
__vue_module_identifier__,
|
|
121
|
+
false,
|
|
122
|
+
undefined,
|
|
123
|
+
undefined,
|
|
124
|
+
undefined
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
export default __vue_component__;
|
|
128
|
+
export { i18n };
|
|
@@ -26,9 +26,6 @@ var script = {
|
|
|
26
26
|
}
|
|
27
27
|
},
|
|
28
28
|
computed: {
|
|
29
|
-
formattedLocalDate() {
|
|
30
|
-
return date => formatLocalizedDate(date, this.preferredLocale);
|
|
31
|
-
},
|
|
32
29
|
groupedThreads() {
|
|
33
30
|
if (!this.hasThreads) {
|
|
34
31
|
return {};
|
|
@@ -46,6 +43,9 @@ var script = {
|
|
|
46
43
|
}
|
|
47
44
|
},
|
|
48
45
|
methods: {
|
|
46
|
+
formattedLocalDate(date) {
|
|
47
|
+
return formatLocalizedDate(date, this.preferredLocale);
|
|
48
|
+
},
|
|
49
49
|
onNewChat() {
|
|
50
50
|
this.$emit('new-chat');
|
|
51
51
|
},
|
|
@@ -63,7 +63,8 @@ var script = {
|
|
|
63
63
|
return new Date(dateA) - new Date(dateB);
|
|
64
64
|
},
|
|
65
65
|
getDateKey(date) {
|
|
66
|
-
|
|
66
|
+
// add 00:00:00 to the date string to make it at midnight local time
|
|
67
|
+
return `${new Date(date).toISOString().split('T')[0]}T00:00:00`;
|
|
67
68
|
},
|
|
68
69
|
sprintf
|
|
69
70
|
},
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export { default as InputRequestedMessage } from './components/chat/components/d
|
|
|
12
12
|
export { default as SystemMessage } from './components/chat/components/duo_chat_message/message_types/message_tool';
|
|
13
13
|
export { default as WorkflowEndMessage } from './components/chat/components/duo_chat_message/message_types/message_workflow_end';
|
|
14
14
|
export { default as AgenticDuoChat } from './components/agentic_chat/agentic_duo_chat';
|
|
15
|
+
export { default as AgenticToolApprovalFlow } from './components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_flow';
|
|
15
16
|
export { default as DuoChatContextItemDetailsModal } from './components/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal';
|
|
16
17
|
export { default as DuoChatContextItemMenu } from './components/chat/components/duo_chat_context/duo_chat_context_item_menu/duo_chat_context_item_menu';
|
|
17
18
|
export { default as DuoChatContextItemPopover } from './components/chat/components/duo_chat_context/duo_chat_context_item_popover/duo_chat_context_item_popover';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitlab/duo-ui",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.22.1",
|
|
4
4
|
"description": "Duo UI Components",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
|
98
98
|
"@babel/preset-env": "^7.27.2",
|
|
99
99
|
"@babel/preset-react": "^7.27.1",
|
|
100
|
-
"@gitlab/eslint-plugin": "
|
|
100
|
+
"@gitlab/eslint-plugin": "21.0.0",
|
|
101
101
|
"@gitlab/fonts": "^1.3.0",
|
|
102
102
|
"@gitlab/stylelint-config": "6.2.2",
|
|
103
103
|
"@gitlab/svgs": "^3.135.0",
|
|
@@ -155,8 +155,8 @@
|
|
|
155
155
|
"module-alias": "^2.2.2",
|
|
156
156
|
"npm-run-all": "^4.1.5",
|
|
157
157
|
"pikaday": "^1.8.0",
|
|
158
|
-
"playwright": "^1.
|
|
159
|
-
"playwright-core": "^1.
|
|
158
|
+
"playwright": "^1.53.0",
|
|
159
|
+
"playwright-core": "^1.53.0",
|
|
160
160
|
"plop": "^2.5.4",
|
|
161
161
|
"postcss": "8.4.28",
|
|
162
162
|
"postcss-loader": "^7.0.2",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
The component provides a complete tool approval flow for Agentic Duo Chat.
|
|
2
|
+
|
|
3
|
+
It orchestrates a two-step approval process where users can approve tool execution
|
|
4
|
+
or deny it with an optional rejection reason.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
To use the component, import it and add it to the `template` part of your consumer component.
|
|
9
|
+
|
|
10
|
+
```html
|
|
11
|
+
<agentic-tool-approval-flow
|
|
12
|
+
:visible="showApproval"
|
|
13
|
+
:tool-details="toolDetails"
|
|
14
|
+
:description="customDescription"
|
|
15
|
+
@approve="handleApprove"
|
|
16
|
+
@deny="handleDeny"
|
|
17
|
+
/>
|
|
18
|
+
```
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { translate } from '../../../../utils/i18n';
|
|
3
|
+
import AgenticToolApprovalModal from './agentic_tool_approval_modal/agentic_tool_approval_modal.vue';
|
|
4
|
+
import AgenticToolRejectionModal from './agentic_tool_rejection_modal/agentic_tool_rejection_modal.vue';
|
|
5
|
+
|
|
6
|
+
export const i18n = {
|
|
7
|
+
DEFAULT_DESCRIPTION: translate(
|
|
8
|
+
'AgenticToolApprovalFlow.defaultDescription',
|
|
9
|
+
'Duo Workflow wants to execute a tool. Do you want to proceed?'
|
|
10
|
+
),
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const MODAL_TYPES = {
|
|
14
|
+
APPROVAL: 'approval',
|
|
15
|
+
REJECTION: 'rejection',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export default {
|
|
19
|
+
name: 'AgenticToolApprovalFlow',
|
|
20
|
+
components: {
|
|
21
|
+
AgenticToolApprovalModal,
|
|
22
|
+
AgenticToolRejectionModal,
|
|
23
|
+
},
|
|
24
|
+
props: {
|
|
25
|
+
/**
|
|
26
|
+
* Controls the visibility of the approval flow
|
|
27
|
+
*/
|
|
28
|
+
visible: {
|
|
29
|
+
type: Boolean,
|
|
30
|
+
required: true,
|
|
31
|
+
},
|
|
32
|
+
/**
|
|
33
|
+
* Details about the tool to be executed
|
|
34
|
+
* @property {string} name - The name of the tool
|
|
35
|
+
* @property {Object} parameters - The parameters to be passed to the tool
|
|
36
|
+
*/
|
|
37
|
+
toolDetails: {
|
|
38
|
+
type: Object,
|
|
39
|
+
required: false,
|
|
40
|
+
default: () => ({}),
|
|
41
|
+
validator: (value) => {
|
|
42
|
+
return typeof value === 'object' && value !== null;
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
/**
|
|
46
|
+
* Custom description text for the approval request
|
|
47
|
+
*/
|
|
48
|
+
description: {
|
|
49
|
+
type: String,
|
|
50
|
+
required: false,
|
|
51
|
+
default: i18n.DEFAULT_DESCRIPTION,
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
data() {
|
|
55
|
+
return {
|
|
56
|
+
currentModal: MODAL_TYPES.APPROVAL,
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
computed: {
|
|
60
|
+
showApprovalModal() {
|
|
61
|
+
return this.visible && this.currentModal === MODAL_TYPES.APPROVAL;
|
|
62
|
+
},
|
|
63
|
+
showRejectionModal() {
|
|
64
|
+
return this.visible && this.currentModal === MODAL_TYPES.REJECTION;
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
watch: {
|
|
68
|
+
visible(newVal) {
|
|
69
|
+
if (!newVal) {
|
|
70
|
+
this.currentModal = MODAL_TYPES.APPROVAL;
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
methods: {
|
|
75
|
+
handleApprove() {
|
|
76
|
+
/**
|
|
77
|
+
* Emitted when the user approves the tool execution
|
|
78
|
+
*/
|
|
79
|
+
this.$emit('approve');
|
|
80
|
+
},
|
|
81
|
+
handleInitialDeny() {
|
|
82
|
+
this.currentModal = MODAL_TYPES.REJECTION;
|
|
83
|
+
},
|
|
84
|
+
handleForceDeny() {
|
|
85
|
+
this.$emit('deny', null);
|
|
86
|
+
},
|
|
87
|
+
handleRejectionSubmit(message) {
|
|
88
|
+
/**
|
|
89
|
+
* Emitted when the user denies the tool execution with an optional reason
|
|
90
|
+
* @param {string|null} reason - The rejection reason provided by the user
|
|
91
|
+
*/
|
|
92
|
+
this.$emit('deny', message);
|
|
93
|
+
},
|
|
94
|
+
handleBack() {
|
|
95
|
+
this.currentModal = MODAL_TYPES.APPROVAL;
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
i18n,
|
|
99
|
+
};
|
|
100
|
+
</script>
|
|
101
|
+
|
|
102
|
+
<template>
|
|
103
|
+
<div data-testid="agentic-tool-approval-flow">
|
|
104
|
+
<agentic-tool-approval-modal
|
|
105
|
+
:visible="showApprovalModal"
|
|
106
|
+
:tool-details="toolDetails"
|
|
107
|
+
:description="description"
|
|
108
|
+
@approve="handleApprove"
|
|
109
|
+
@deny="handleInitialDeny"
|
|
110
|
+
@deny-force="handleForceDeny"
|
|
111
|
+
/>
|
|
112
|
+
|
|
113
|
+
<agentic-tool-rejection-modal
|
|
114
|
+
:visible="showRejectionModal"
|
|
115
|
+
@submit="handleRejectionSubmit"
|
|
116
|
+
@back="handleBack"
|
|
117
|
+
/>
|
|
118
|
+
</div>
|
|
119
|
+
</template>
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { GlModal } from '@gitlab/ui';
|
|
3
|
+
import { translate } from '../../../../../utils/i18n';
|
|
4
|
+
|
|
5
|
+
export const i18n = {
|
|
6
|
+
TITLE: translate('AgenticToolApprovalModal.title', 'Tool Approval Required'),
|
|
7
|
+
DEFAULT_DESCRIPTION: translate(
|
|
8
|
+
'AgenticToolApprovalModal.defaultDescription',
|
|
9
|
+
'Duo Workflow wants to execute a tool. Do you want to proceed?'
|
|
10
|
+
),
|
|
11
|
+
TOOL_LABEL: translate('AgenticToolApprovalModal.toolLabel', 'Tool:'),
|
|
12
|
+
TOOL_UNKNOWN: translate('AgenticToolApprovalModal.toolUnknown', 'Unknown'),
|
|
13
|
+
APPROVE_TEXT: translate('AgenticToolApprovalModal.approveText', 'Approve'),
|
|
14
|
+
DENY_TEXT: translate('AgenticToolApprovalModal.denyText', 'Deny'),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default {
|
|
18
|
+
name: 'AgenticToolApprovalModal',
|
|
19
|
+
components: {
|
|
20
|
+
GlModal,
|
|
21
|
+
},
|
|
22
|
+
props: {
|
|
23
|
+
/**
|
|
24
|
+
* Controls the visibility of the modal
|
|
25
|
+
*/
|
|
26
|
+
visible: {
|
|
27
|
+
type: Boolean,
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
/**
|
|
31
|
+
* Details about the tool to be executed
|
|
32
|
+
* @property {string} name - The name of the tool
|
|
33
|
+
* @property {Object} parameters - The parameters to be passed to the tool
|
|
34
|
+
*/
|
|
35
|
+
toolDetails: {
|
|
36
|
+
type: Object,
|
|
37
|
+
required: false,
|
|
38
|
+
default: () => ({}),
|
|
39
|
+
validator: (value) => {
|
|
40
|
+
return typeof value === 'object' && value !== null;
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
/**
|
|
44
|
+
* Custom description text for the approval request
|
|
45
|
+
*/
|
|
46
|
+
description: {
|
|
47
|
+
type: String,
|
|
48
|
+
required: false,
|
|
49
|
+
default: i18n.DEFAULT_DESCRIPTION,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
computed: {
|
|
53
|
+
approveAction() {
|
|
54
|
+
return {
|
|
55
|
+
text: this.$options.i18n.APPROVE_TEXT,
|
|
56
|
+
attributes: {
|
|
57
|
+
variant: 'confirm',
|
|
58
|
+
icon: 'play',
|
|
59
|
+
size: 'small',
|
|
60
|
+
'data-testid': 'approve-tool',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
denyAction() {
|
|
65
|
+
return {
|
|
66
|
+
text: this.$options.i18n.DENY_TEXT,
|
|
67
|
+
attributes: {
|
|
68
|
+
variant: 'danger',
|
|
69
|
+
icon: 'cancel',
|
|
70
|
+
size: 'small',
|
|
71
|
+
'data-testid': 'deny-tool',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
toolName() {
|
|
76
|
+
return this.toolDetails?.name || this.$options.i18n.TOOL_UNKNOWN;
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
methods: {
|
|
80
|
+
handleApprove() {
|
|
81
|
+
/**
|
|
82
|
+
* Emitted when the user approves the tool execution
|
|
83
|
+
*/
|
|
84
|
+
this.$emit('approve');
|
|
85
|
+
},
|
|
86
|
+
handleDeny() {
|
|
87
|
+
/**
|
|
88
|
+
* Emitted when the user denies the tool execution
|
|
89
|
+
*/
|
|
90
|
+
this.$emit('deny');
|
|
91
|
+
},
|
|
92
|
+
handleModalHide(event) {
|
|
93
|
+
// If the modal is being hidden by the X button or ESC key,
|
|
94
|
+
// treat it as a deny-force action
|
|
95
|
+
if (event.trigger !== 'ok' && event.trigger !== 'cancel') {
|
|
96
|
+
this.$emit('deny-force');
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
i18n,
|
|
101
|
+
};
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<template>
|
|
105
|
+
<gl-modal
|
|
106
|
+
:visible="visible"
|
|
107
|
+
modal-id="agentic-tool-approval-modal"
|
|
108
|
+
:title="$options.i18n.TITLE"
|
|
109
|
+
:action-primary="approveAction"
|
|
110
|
+
:action-cancel="denyAction"
|
|
111
|
+
:no-close-on-backdrop="true"
|
|
112
|
+
:no-close-on-esc="true"
|
|
113
|
+
data-testid="agentic-tool-approval-modal"
|
|
114
|
+
@primary="handleApprove"
|
|
115
|
+
@canceled="handleDeny"
|
|
116
|
+
@hide="handleModalHide"
|
|
117
|
+
>
|
|
118
|
+
<div class="gl-mb-4">
|
|
119
|
+
<p class="gl-mb-3" data-testid="approval-description">
|
|
120
|
+
{{ description }}
|
|
121
|
+
</p>
|
|
122
|
+
|
|
123
|
+
<div
|
|
124
|
+
v-if="toolDetails"
|
|
125
|
+
class="gl-bg-gray-10 gl-p-3 gl-rounded-base gl-mb-3"
|
|
126
|
+
data-testid="tool-details"
|
|
127
|
+
>
|
|
128
|
+
<strong>{{ $options.i18n.TOOL_LABEL }} {{ toolName }}</strong>
|
|
129
|
+
<pre
|
|
130
|
+
v-if="toolDetails.parameters"
|
|
131
|
+
class="gl-mt-2 gl-mb-0 gl-text-inherit"
|
|
132
|
+
data-testid="tool-parameters"
|
|
133
|
+
>{{ JSON.stringify(toolDetails.parameters, null, 2) }}</pre
|
|
134
|
+
>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</gl-modal>
|
|
138
|
+
</template>
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { GlModal, GlFormTextarea, GlFormGroup } from '@gitlab/ui';
|
|
3
|
+
import { translate } from '../../../../../utils/i18n';
|
|
4
|
+
|
|
5
|
+
export const i18n = {
|
|
6
|
+
TITLE: translate('AgenticToolRejectionModal.title', 'Provide Rejection Reason'),
|
|
7
|
+
DESCRIPTION: translate(
|
|
8
|
+
'AgenticToolRejectionModal.description',
|
|
9
|
+
'You are about to reject the tool execution. Would you like to provide a reason?'
|
|
10
|
+
),
|
|
11
|
+
LABEL: translate('AgenticToolRejectionModal.label', 'Rejection reason'),
|
|
12
|
+
PLACEHOLDER: translate(
|
|
13
|
+
'AgenticToolRejectionModal.placeholder',
|
|
14
|
+
"Explain why you're rejecting this tool execution..."
|
|
15
|
+
),
|
|
16
|
+
SUBMIT_TEXT: translate('AgenticToolRejectionModal.submitText', 'Submit Rejection'),
|
|
17
|
+
BACK_TEXT: translate('AgenticToolRejectionModal.backText', 'Back'),
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default {
|
|
21
|
+
name: 'AgenticToolRejectionModal',
|
|
22
|
+
components: {
|
|
23
|
+
GlModal,
|
|
24
|
+
GlFormTextarea,
|
|
25
|
+
GlFormGroup,
|
|
26
|
+
},
|
|
27
|
+
props: {
|
|
28
|
+
/**
|
|
29
|
+
* Controls the visibility of the modal
|
|
30
|
+
*/
|
|
31
|
+
visible: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
required: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
data() {
|
|
37
|
+
return {
|
|
38
|
+
rejectionMessage: '',
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
computed: {
|
|
42
|
+
submitAction() {
|
|
43
|
+
return {
|
|
44
|
+
text: this.$options.i18n.SUBMIT_TEXT,
|
|
45
|
+
attributes: {
|
|
46
|
+
variant: 'danger',
|
|
47
|
+
icon: 'cancel',
|
|
48
|
+
size: 'small',
|
|
49
|
+
'data-testid': 'submit-rejection',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
},
|
|
53
|
+
backAction() {
|
|
54
|
+
return {
|
|
55
|
+
text: this.$options.i18n.BACK_TEXT,
|
|
56
|
+
attributes: {
|
|
57
|
+
variant: 'default',
|
|
58
|
+
icon: 'chevron-left',
|
|
59
|
+
size: 'small',
|
|
60
|
+
'data-testid': 'back-button',
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
watch: {
|
|
66
|
+
visible(newVal) {
|
|
67
|
+
if (!newVal) {
|
|
68
|
+
this.rejectionMessage = '';
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
methods: {
|
|
73
|
+
handleSubmit() {
|
|
74
|
+
/**
|
|
75
|
+
* Emitted when the user submits the rejection reason
|
|
76
|
+
* @param {string|null} reason - The rejection reason provided by the user, or null if empty
|
|
77
|
+
*/
|
|
78
|
+
this.$emit('submit', this.rejectionMessage || null);
|
|
79
|
+
this.rejectionMessage = '';
|
|
80
|
+
},
|
|
81
|
+
handleBack() {
|
|
82
|
+
/**
|
|
83
|
+
* Emitted when the user clicks the back button to return to the approval modal
|
|
84
|
+
*/
|
|
85
|
+
this.$emit('back');
|
|
86
|
+
},
|
|
87
|
+
handleModalHide(event) {
|
|
88
|
+
// If the modal is being hidden by the X button or ESC key,
|
|
89
|
+
// treat it as a deny-force action
|
|
90
|
+
if (event.trigger !== 'ok' && event.trigger !== 'cancel') {
|
|
91
|
+
this.handleBack();
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
i18n,
|
|
96
|
+
};
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<template>
|
|
100
|
+
<gl-modal
|
|
101
|
+
:visible="visible"
|
|
102
|
+
modal-id="agentic-tool-rejection-modal"
|
|
103
|
+
:title="$options.i18n.TITLE"
|
|
104
|
+
:action-primary="submitAction"
|
|
105
|
+
:action-secondary="backAction"
|
|
106
|
+
:no-close-on-backdrop="true"
|
|
107
|
+
:no-close-on-esc="true"
|
|
108
|
+
data-testid="agentic-tool-rejection-modal"
|
|
109
|
+
@primary="handleSubmit"
|
|
110
|
+
@secondary="handleBack"
|
|
111
|
+
@hide="handleModalHide"
|
|
112
|
+
>
|
|
113
|
+
<p class="gl-mb-3" data-testid="rejection-description">
|
|
114
|
+
{{ $options.i18n.DESCRIPTION }}
|
|
115
|
+
</p>
|
|
116
|
+
<gl-form-group
|
|
117
|
+
:label="$options.i18n.LABEL"
|
|
118
|
+
label-for="rejection-message"
|
|
119
|
+
:optional="true"
|
|
120
|
+
class="gl-mb-0"
|
|
121
|
+
>
|
|
122
|
+
<gl-form-textarea
|
|
123
|
+
id="rejection-message"
|
|
124
|
+
v-model="rejectionMessage"
|
|
125
|
+
:placeholder="$options.i18n.PLACEHOLDER"
|
|
126
|
+
:rows="3"
|
|
127
|
+
:max-rows="6"
|
|
128
|
+
:resize="false"
|
|
129
|
+
data-testid="rejection-message-input"
|
|
130
|
+
autofocus
|
|
131
|
+
/>
|
|
132
|
+
</gl-form-group>
|
|
133
|
+
</gl-modal>
|
|
134
|
+
</template>
|
|
@@ -33,10 +33,6 @@ export default {
|
|
|
33
33
|
},
|
|
34
34
|
|
|
35
35
|
computed: {
|
|
36
|
-
formattedLocalDate() {
|
|
37
|
-
return (date) => formatLocalizedDate(date, this.preferredLocale);
|
|
38
|
-
},
|
|
39
|
-
|
|
40
36
|
groupedThreads() {
|
|
41
37
|
if (!this.hasThreads) {
|
|
42
38
|
return {};
|
|
@@ -53,38 +49,35 @@ export default {
|
|
|
53
49
|
};
|
|
54
50
|
}, {});
|
|
55
51
|
},
|
|
56
|
-
|
|
57
52
|
hasThreads() {
|
|
58
53
|
return this.threads.length > 0;
|
|
59
54
|
},
|
|
60
55
|
},
|
|
61
56
|
|
|
62
57
|
methods: {
|
|
58
|
+
formattedLocalDate(date) {
|
|
59
|
+
return formatLocalizedDate(date, this.preferredLocale);
|
|
60
|
+
},
|
|
63
61
|
onNewChat() {
|
|
64
62
|
this.$emit('new-chat');
|
|
65
63
|
},
|
|
66
|
-
|
|
67
64
|
onSelectThread(thread) {
|
|
68
65
|
this.$emit('select-thread', thread);
|
|
69
66
|
},
|
|
70
|
-
|
|
71
67
|
onDeleteThread(threadId, event) {
|
|
72
68
|
event.stopPropagation();
|
|
73
69
|
this.$emit('delete-thread', threadId);
|
|
74
70
|
},
|
|
75
|
-
|
|
76
71
|
onClose() {
|
|
77
72
|
this.$emit('close');
|
|
78
73
|
},
|
|
79
|
-
|
|
80
74
|
compareThreadDates(dateA, dateB) {
|
|
81
75
|
return new Date(dateA) - new Date(dateB);
|
|
82
76
|
},
|
|
83
|
-
|
|
84
77
|
getDateKey(date) {
|
|
85
|
-
|
|
78
|
+
// add 00:00:00 to the date string to make it at midnight local time
|
|
79
|
+
return `${new Date(date).toISOString().split('T')[0]}T00:00:00`;
|
|
86
80
|
},
|
|
87
|
-
|
|
88
81
|
sprintf,
|
|
89
82
|
},
|
|
90
83
|
i18n,
|
package/src/index.js
CHANGED
|
@@ -20,6 +20,7 @@ export { default as WorkflowEndMessage } from './components/chat/components/duo_
|
|
|
20
20
|
|
|
21
21
|
// Agentic Duo Chat component
|
|
22
22
|
export { default as AgenticDuoChat } from './components/agentic_chat/agentic_duo_chat.vue';
|
|
23
|
+
export { default as AgenticToolApprovalFlow } from './components/agentic_chat/components/agentic_tool_approval_flow/agentic_tool_approval_flow.vue';
|
|
23
24
|
|
|
24
25
|
// Duo Chat Context components
|
|
25
26
|
export { default as DuoChatContextItemDetailsModal } from './components/chat/components/duo_chat_context/duo_chat_context_item_details_modal/duo_chat_context_item_details_modal.vue';
|
package/translations.js
CHANGED
|
@@ -15,6 +15,22 @@ export default {
|
|
|
15
15
|
'AgenticDuoChat.chatPromptPlaceholderDefault': 'GitLab Duo Chat',
|
|
16
16
|
'AgenticDuoChat.chatPromptPlaceholderWithCommands': 'Type /help to learn more',
|
|
17
17
|
'AgenticDuoChat.chatSubmitLabel': 'Send chat message.',
|
|
18
|
+
'AgenticToolApprovalFlow.defaultDescription':
|
|
19
|
+
'Duo Workflow wants to execute a tool. Do you want to proceed?',
|
|
20
|
+
'AgenticToolApprovalModal.approveText': 'Approve',
|
|
21
|
+
'AgenticToolApprovalModal.defaultDescription':
|
|
22
|
+
'Duo Workflow wants to execute a tool. Do you want to proceed?',
|
|
23
|
+
'AgenticToolApprovalModal.denyText': 'Deny',
|
|
24
|
+
'AgenticToolApprovalModal.title': 'Tool Approval Required',
|
|
25
|
+
'AgenticToolApprovalModal.toolLabel': 'Tool:',
|
|
26
|
+
'AgenticToolApprovalModal.toolUnknown': 'Unknown',
|
|
27
|
+
'AgenticToolRejectionModal.backText': 'Back',
|
|
28
|
+
'AgenticToolRejectionModal.description':
|
|
29
|
+
'You are about to reject the tool execution. Would you like to provide a reason?',
|
|
30
|
+
'AgenticToolRejectionModal.label': 'Rejection reason',
|
|
31
|
+
'AgenticToolRejectionModal.placeholder': "Explain why you're rejecting this tool execution...",
|
|
32
|
+
'AgenticToolRejectionModal.submitText': 'Submit Rejection',
|
|
33
|
+
'AgenticToolRejectionModal.title': 'Provide Rejection Reason',
|
|
18
34
|
'DuoChat.chatBackLabel': 'Back to history',
|
|
19
35
|
'DuoChat.chatBackToChatToolTip': 'Back to chat',
|
|
20
36
|
'DuoChat.chatCancelLabel': 'Cancel',
|