@electerm/electerm-react 3.15.159 → 3.15.160

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.
@@ -1,11 +1,12 @@
1
1
  import { agentTools, executeToolCall } from './agent-tools'
2
+ import { appendMandatoryGuardrails } from './ai-guardrails'
2
3
 
3
4
  const MAX_ITERATIONS = 150
4
5
 
5
6
  function buildAgentSystemPrompt (config) {
6
7
  const lang = config.languageAI || window.store.getLangName()
7
8
  const baseRole = config.roleAI || 'You are a helpful assistant.'
8
- return `${baseRole}
9
+ return appendMandatoryGuardrails(`${baseRole}
9
10
 
10
11
  You are operating inside electerm, a terminal/SSH client. You have access to tools that let you:
11
12
  - Run commands in terminal tabs and read their output
@@ -21,7 +22,7 @@ Prefer using the active terminal unless the user specifies otherwise.
21
22
  For SSH connections, prefer using open_tab to connect directly, or create a bookmark with add_bookmark and open it with open_bookmark if the user wants to save the connection.
22
23
  For file transfers, use the sftp_upload and sftp_download tools. The tab must be an SSH/FTP connection with SFTP initialized.
23
24
 
24
- Reply in ${lang} language.`
25
+ Reply in ${lang} language.`)
25
26
  }
26
27
 
27
28
  function updateChatEntry (chatEntry, updates) {
@@ -3,6 +3,7 @@ import AIOutput from './ai-output'
3
3
  import AIStopIcon from './ai-stop-icon'
4
4
  import AgentToolCallCard from './agent-tool-call-card'
5
5
  import { runAgentLoop } from './agent'
6
+ import { appendMandatoryGuardrails } from './ai-guardrails'
6
7
  import {
7
8
  Alert,
8
9
  Tooltip
@@ -42,7 +43,7 @@ export default function AIChatHistoryItem ({ item }) {
42
43
 
43
44
  function buildRole () {
44
45
  const lang = languageAI || window.store.getLangName()
45
- return roleAI + `;用[${lang}]回复`
46
+ return appendMandatoryGuardrails(roleAI + `;用[${lang}]回复`)
46
47
  }
47
48
 
48
49
  const conversationMessages = useMemo(() => {
@@ -253,6 +253,9 @@ export default function AIChat (props) {
253
253
  {renderSendIcon()}
254
254
  </Flex>
255
255
  </Flex>
256
+ {window.et.AIDisclamer && (
257
+ <div className='ai-disclamer mg1t'>{window.et.AIDisclamer}</div>
258
+ )}
256
259
  </Flex>
257
260
  )
258
261
  }
@@ -18,6 +18,7 @@ import Password from '../common/password'
18
18
  import AiHistory, { addHistoryItem } from './ai-history'
19
19
  import message from '../common/message'
20
20
  import { getAIPresets } from './ai-config-props'
21
+ import { appendMandatoryGuardrails } from './ai-guardrails'
21
22
 
22
23
  const STORAGE_KEY_CONFIG = 'ai_config_history'
23
24
  const EVENT_NAME_CONFIG = 'ai-config-history-update'
@@ -74,7 +75,7 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
74
75
  'AIchat',
75
76
  'Hi',
76
77
  values.modelAI,
77
- values.roleAI,
78
+ appendMandatoryGuardrails(values.roleAI),
78
79
  values.baseURLAI,
79
80
  values.apiPathAI,
80
81
  values.apiKeyAI,
@@ -164,7 +165,14 @@ export default function AIConfigForm ({ initialValues, onSubmit, showAIConfig })
164
165
  <Link to={aiConfigWikiLink}>WIKI: {aiConfigWikiLink}</Link>
165
166
  }
166
167
  type='info'
167
- className='mg2y'
168
+ className='mg2t mg1b'
169
+ />
170
+ <Alert
171
+ title={
172
+ window.translate('aiWarn')
173
+ }
174
+ type='warning'
175
+ className='mg2b'
168
176
  />
169
177
  <div className='mg1b alignright'>
170
178
  {renderPresetMenu()}
@@ -0,0 +1,10 @@
1
+ // Append mandatory guardrails (if configured via window.et.mandatoryGuardrails)
2
+ // to the AI system role as an extra layer of protection. This is typically set
3
+ // by enterprise deployments to enforce policies on every AI request.
4
+ export function appendMandatoryGuardrails (systemContent) {
5
+ const guardrails = window.et && window.et.mandatoryGuardrails
6
+ if (!guardrails) {
7
+ return systemContent
8
+ }
9
+ return `${systemContent}\n\n${guardrails}`
10
+ }
@@ -2,12 +2,19 @@ import { useRef, useEffect } from 'react'
2
2
  import ReactMarkdown from 'react-markdown'
3
3
  import { copy } from '../../common/clipboard'
4
4
  import Link from '../common/external-link'
5
- import { Tag } from 'antd'
6
- import { CopyOutlined, PlayCircleOutlined } from '@ant-design/icons'
5
+ import { Tag, Popconfirm, Button, Alert } from 'antd'
6
+ import {
7
+ CopyOutlined,
8
+ PlayCircleOutlined,
9
+ FlagOutlined,
10
+ FlagFilled
11
+ } from '@ant-design/icons'
7
12
  import getBrand from './get-brand'
8
13
 
9
14
  const e = window.translate
10
15
 
16
+ const enableAIFlag = !!(window.et && window.et.enableAIFlag)
17
+
11
18
  export default function AIOutput ({ item }) {
12
19
  const outputRef = useRef(null)
13
20
  const {
@@ -23,7 +30,7 @@ export default function AIOutput ({ item }) {
23
30
  }
24
31
  }, [response])
25
32
 
26
- if (!response) {
33
+ if (!response && !(item.flagged && enableAIFlag)) {
27
34
  return null
28
35
  }
29
36
 
@@ -89,6 +96,38 @@ export default function AIOutput ({ item }) {
89
96
  )
90
97
  }
91
98
 
99
+ function handleToggleFlag () {
100
+ const index = window.store.aiChatHistory.findIndex(i => i.id === item.id)
101
+ if (index === -1) {
102
+ return
103
+ }
104
+ window.store.aiChatHistory[index].flagged = !window.store.aiChatHistory[index].flagged
105
+ window.store.aiChatHistory = [...window.store.aiChatHistory]
106
+ }
107
+
108
+ function renderFlag () {
109
+ if (!enableAIFlag) {
110
+ return null
111
+ }
112
+ return (
113
+ <div className={'ai-stream-output-flag' + (item.flagged ? ' is-flagged' : '')}>
114
+ <Popconfirm
115
+ title={item.flagged ? 'Remove the harmful-info flag?' : 'Flag this as harmful info?'}
116
+ okText='Confirm'
117
+ cancelText='Cancel'
118
+ onConfirm={handleToggleFlag}
119
+ >
120
+ <Button
121
+ size='small'
122
+ type='text'
123
+ icon={item.flagged ? <FlagFilled /> : <FlagOutlined />}
124
+ onClick={(evt) => evt.stopPropagation()}
125
+ />
126
+ </Popconfirm>
127
+ </div>
128
+ )
129
+ }
130
+
92
131
  function renderBrand () {
93
132
  if (!brand) {
94
133
  return null
@@ -113,9 +152,16 @@ export default function AIOutput ({ item }) {
113
152
 
114
153
  return (
115
154
  <div className='ai-stream-output' ref={outputRef}>
155
+ {renderFlag()}
116
156
  <div className='pd1'>
117
- {renderBrand()}
118
- <ReactMarkdown {...mdProps} />
157
+ {item.flagged && enableAIFlag
158
+ ? <Alert type='warning' message='user flagged as harmful info' />
159
+ : (
160
+ <>
161
+ {renderBrand()}
162
+ <ReactMarkdown {...mdProps} />
163
+ </>
164
+ )}
119
165
  </div>
120
166
  </div>
121
167
  )
@@ -33,8 +33,17 @@
33
33
  // display block
34
34
 
35
35
  .ai-stream-output
36
+ position relative
36
37
  max-height 400px
37
38
  overflow-y auto
39
+ &::-webkit-scrollbar-thumb
40
+ background var(--main-dark)
41
+ .ai-stream-output-flag
42
+ position absolute
43
+ top 13px
44
+ right 10px
45
+ z-index 10
46
+
38
47
 
39
48
  .ai-chat-input
40
49
  position relative
@@ -52,6 +61,9 @@
52
61
  z-index 1
53
62
  font-size 16px
54
63
 
64
+ .ai-disclamer
65
+ font-size 12px
66
+
55
67
  .code-block
56
68
  width 100%
57
69
  pre
@@ -69,6 +81,7 @@
69
81
 
70
82
  .ai-chat-sessions-wrap
71
83
  width 100%
84
+ height 100%
72
85
  overflow-y auto
73
86
  overflow-x hidden
74
87
  padding 4px 0
@@ -18,6 +18,7 @@ import {
18
18
  import SimpleEditor from '../text-editor/simple-editor'
19
19
  import { copy } from '../../common/clipboard'
20
20
  import download from '../../common/download'
21
+ import { appendMandatoryGuardrails } from '../ai/ai-guardrails'
21
22
  import AICategorySelect from './common/ai-category-select.jsx'
22
23
  import HelpIcon from '../common/help-icon'
23
24
  import Modal from '../common/modal.jsx'
@@ -77,7 +78,7 @@ export default function AIBookmarkForm (props) {
77
78
  'AIchat',
78
79
  prompt,
79
80
  config.modelAI,
80
- 'You are a helpful assistant that generates bookmark configurations in JSON format.',
81
+ appendMandatoryGuardrails('You are a helpful assistant that generates bookmark configurations in JSON format.'),
81
82
  config.baseURLAI,
82
83
  config.apiPathAI,
83
84
  config.apiKeyAI,
@@ -2,6 +2,7 @@ import { Component } from 'manate/react/class-components'
2
2
  import { refsStatic, refs } from '../common/ref'
3
3
  import SuggestionItem from './cmd-item'
4
4
  import { aiSuggestionsCache } from '../../common/cache'
5
+ import { appendMandatoryGuardrails } from '../ai/ai-guardrails'
5
6
  import classnames from 'classnames'
6
7
  import {
7
8
  LoadingOutlined
@@ -67,7 +68,7 @@ export default class TerminalCmdSuggestions extends Component {
67
68
  'AIchat',
68
69
  prompt,
69
70
  config.modelAI,
70
- config.roleAI,
71
+ appendMandatoryGuardrails(config.roleAI),
71
72
  config.baseURLAI,
72
73
  config.apiPathAI,
73
74
  config.apiKeyAI,
@@ -4,6 +4,7 @@
4
4
 
5
5
  import handleError from '../common/error-handler'
6
6
  import Modal from '../components/common/modal'
7
+ import { appendMandatoryGuardrails } from '../components/ai/ai-guardrails'
7
8
  import { debounce, some, get, pickBy } from 'lodash-es'
8
9
  import {
9
10
  leftSidebarWidthKey,
@@ -106,7 +107,7 @@ export default Store => {
106
107
 
107
108
  Store.prototype.setLeftSidePanelWidth = function (v) {
108
109
  ls.setItem(leftSidebarWidthKey, v)
109
- window.store.leftSidebarWidth = v
110
+ window.store._leftSidebarWidth = v
110
111
  }
111
112
 
112
113
  Store.prototype.setAddPanelWidth = function (v) {
@@ -116,7 +117,7 @@ export default Store => {
116
117
 
117
118
  Store.prototype.setRightSidePanelWidth = function (v) {
118
119
  ls.setItem(rightSidebarWidthKey, v)
119
- window.store.rightPanelWidth = v
120
+ window.store._rightPanelWidth = v
120
121
  }
121
122
  Store.prototype.dismissDelKeyTip = function (v) {
122
123
  ls.setItem(dismissDelKeyTipLsKey, 'y')
@@ -322,7 +323,7 @@ export default Store => {
322
323
  const firstEntry = sessionEntries[0]
323
324
  const lang = firstEntry.languageAI || store.getLangName()
324
325
  const messages = [
325
- { role: 'system', content: firstEntry.roleAI + `;用[${lang}]回复` }
326
+ { role: 'system', content: appendMandatoryGuardrails(firstEntry.roleAI + `;用[${lang}]回复`) }
326
327
  ]
327
328
 
328
329
  // Start from the last compress entry to include its summary as context
@@ -123,7 +123,7 @@ export default () => {
123
123
  rightPanelVisible: false,
124
124
  rightPanelTab: 'info',
125
125
  rightPanelPinned: false,
126
- rightPanelWidth: parseInt(ls.getItem(rightSidebarWidthKey), 10) || 500,
126
+ _rightPanelWidth: parseInt(ls.getItem(rightSidebarWidthKey), 10) || 500,
127
127
  showAIConfigModal: false,
128
128
 
129
129
  // for settings related
@@ -164,7 +164,7 @@ export default () => {
164
164
 
165
165
  // sidebar
166
166
  openedSideBar: ls.getItem(openedSidebarKey) || '',
167
- leftSidebarWidth: parseInt(ls.getItem(leftSidebarWidthKey), 10) || 300,
167
+ _leftSidebarWidth: parseInt(ls.getItem(leftSidebarWidthKey), 10) || 300,
168
168
  addPanelWidth: parseInt(ls.getItem(addPanelWidthLsKey), 10) || 300,
169
169
  menuOpened: false,
170
170
  pinned: ls.getItem(sidebarPinnedKey) === 'true',
@@ -37,7 +37,8 @@ import {
37
37
  terminalSshConfigType,
38
38
  paneMap,
39
39
  staticNewItemTabs,
40
- isWin
40
+ isWin,
41
+ sidebarWidth
41
42
  } from '../common/constants'
42
43
  import getInitItem from '../common/init-setting-item'
43
44
  import createTitle from '../common/create-title'
@@ -58,6 +59,22 @@ class Store {
58
59
  return window.store.innerWidth
59
60
  }
60
61
 
62
+ get leftSidebarWidth () {
63
+ const { store } = window
64
+ if (store.isMobile) {
65
+ return Math.min(store.innerWidth - sidebarWidth, 500)
66
+ }
67
+ return store._leftSidebarWidth
68
+ }
69
+
70
+ get rightPanelWidth () {
71
+ const { store } = window
72
+ if (store.isMobile) {
73
+ return Math.min(store.innerWidth, 500)
74
+ }
75
+ return store._rightPanelWidth
76
+ }
77
+
61
78
  get config () {
62
79
  return deepCopy(window.store._config)
63
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@electerm/electerm-react",
3
- "version": "3.15.159",
3
+ "version": "3.15.160",
4
4
  "description": "react components src for electerm",
5
5
  "main": "./client/components/main/main.jsx",
6
6
  "license": "MIT",