@stack-spot/ai-chat-widget 1.13.2-beta.0 → 1.14.0-beta.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.
Files changed (185) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/StackspotAIWidget.d.ts.map +1 -1
  3. package/dist/StackspotAIWidget.js +3 -2
  4. package/dist/StackspotAIWidget.js.map +1 -1
  5. package/dist/app-metadata.json +3 -3
  6. package/dist/chat-interceptors/CustomInputs.d.ts +2 -2
  7. package/dist/chat-interceptors/CustomInputs.d.ts.map +1 -1
  8. package/dist/chat-interceptors/CustomInputs.js.map +1 -1
  9. package/dist/chat-interceptors/quick-commands.js.map +1 -1
  10. package/dist/chat-interceptors/send-message.d.ts.map +1 -1
  11. package/dist/chat-interceptors/send-message.js +2 -0
  12. package/dist/chat-interceptors/send-message.js.map +1 -1
  13. package/dist/components/Markdown.d.ts.map +1 -1
  14. package/dist/components/Markdown.js +1 -1
  15. package/dist/components/Markdown.js.map +1 -1
  16. package/dist/components/StackedBadge.d.ts +14 -0
  17. package/dist/components/StackedBadge.d.ts.map +1 -0
  18. package/dist/components/StackedBadge.js +56 -0
  19. package/dist/components/StackedBadge.js.map +1 -0
  20. package/dist/components/ToolBadge.d.ts +14 -0
  21. package/dist/components/ToolBadge.d.ts.map +1 -0
  22. package/dist/components/ToolBadge.js +108 -0
  23. package/dist/components/ToolBadge.js.map +1 -0
  24. package/dist/index.d.ts +1 -1
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/state/ChatEntry.d.ts +12 -19
  27. package/dist/state/ChatEntry.d.ts.map +1 -1
  28. package/dist/state/ChatEntry.js.map +1 -1
  29. package/dist/state/ChatState.d.ts +2 -5
  30. package/dist/state/ChatState.d.ts.map +1 -1
  31. package/dist/state/WidgetState.d.ts +3 -4
  32. package/dist/state/WidgetState.d.ts.map +1 -1
  33. package/dist/state/WidgetState.js +1 -12
  34. package/dist/state/WidgetState.js.map +1 -1
  35. package/dist/state/types.d.ts +3 -1
  36. package/dist/state/types.d.ts.map +1 -1
  37. package/dist/utils/tools.d.ts +7 -0
  38. package/dist/utils/tools.d.ts.map +1 -0
  39. package/dist/utils/tools.js +9 -0
  40. package/dist/utils/tools.js.map +1 -0
  41. package/dist/views/Agents/AgentDescription.d.ts.map +1 -1
  42. package/dist/views/Agents/AgentDescription.js +11 -1
  43. package/dist/views/Agents/AgentDescription.js.map +1 -1
  44. package/dist/views/Agents/AgentsTab.d.ts.map +1 -1
  45. package/dist/views/Agents/AgentsTab.js +4 -8
  46. package/dist/views/Agents/AgentsTab.js.map +1 -1
  47. package/dist/views/Agents/dictionary.d.ts +1 -1
  48. package/dist/views/Agents/dictionary.d.ts.map +1 -1
  49. package/dist/views/Agents/dictionary.js +2 -0
  50. package/dist/views/Agents/dictionary.js.map +1 -1
  51. package/dist/views/Agents/styled.js +1 -1
  52. package/dist/views/Chat/AgentInfo.d.ts +2 -2
  53. package/dist/views/Chat/AgentInfo.d.ts.map +1 -1
  54. package/dist/views/Chat/AgentInfo.js +3 -3
  55. package/dist/views/Chat/AgentInfo.js.map +1 -1
  56. package/dist/views/Chat/ChatMessage.d.ts.map +1 -1
  57. package/dist/views/Chat/ChatMessage.js +22 -4
  58. package/dist/views/Chat/ChatMessage.js.map +1 -1
  59. package/dist/views/Chat/StepsList.d.ts +2 -2
  60. package/dist/views/Chat/StepsList.d.ts.map +1 -1
  61. package/dist/views/Chat/StepsList.js +2 -2
  62. package/dist/views/Chat/StepsList.js.map +1 -1
  63. package/dist/views/Chat/styled.d.ts.map +1 -1
  64. package/dist/views/Chat/styled.js +21 -12
  65. package/dist/views/Chat/styled.js.map +1 -1
  66. package/dist/views/ChatHistory/utils.d.ts +2 -2
  67. package/dist/views/ChatHistory/utils.d.ts.map +1 -1
  68. package/dist/views/ChatTabSelection.d.ts.map +1 -1
  69. package/dist/views/ChatTabSelection.js +16 -2
  70. package/dist/views/ChatTabSelection.js.map +1 -1
  71. package/dist/views/MessageInput/AgentSelector.d.ts.map +1 -1
  72. package/dist/views/MessageInput/AgentSelector.js +5 -9
  73. package/dist/views/MessageInput/AgentSelector.js.map +1 -1
  74. package/dist/views/MessageInput/ButtonAgent.d.ts.map +1 -1
  75. package/dist/views/MessageInput/ButtonAgent.js +21 -2
  76. package/dist/views/MessageInput/ButtonAgent.js.map +1 -1
  77. package/dist/views/MessageInput/index.js +2 -2
  78. package/dist/views/MessageInput/index.js.map +1 -1
  79. package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.d.ts.map +1 -1
  80. package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.js.map +1 -1
  81. package/dist/views/{Tools → Steps}/FlowChart/NodeStep.d.ts.map +1 -1
  82. package/dist/views/Steps/FlowChart/NodeStep.js +13 -0
  83. package/dist/views/Steps/FlowChart/NodeStep.js.map +1 -0
  84. package/dist/views/Steps/FlowChart/index.d.ts +10 -0
  85. package/dist/views/Steps/FlowChart/index.d.ts.map +1 -0
  86. package/dist/views/{Tools → Steps}/FlowChart/index.js +1 -5
  87. package/dist/views/{Tools → Steps}/FlowChart/index.js.map +1 -1
  88. package/dist/views/{Tools → Steps}/FlowChart/layout.d.ts.map +1 -1
  89. package/dist/views/{Tools → Steps}/FlowChart/layout.js.map +1 -1
  90. package/dist/views/{Tools → Steps}/FlowChart/styled.d.ts.map +1 -1
  91. package/dist/views/{Tools → Steps}/FlowChart/styled.js +0 -49
  92. package/dist/views/{Tools → Steps}/FlowChart/styled.js.map +1 -1
  93. package/dist/views/{Tools → Steps}/FlowChart/types.d.ts +3 -3
  94. package/dist/views/Steps/FlowChart/types.d.ts.map +1 -0
  95. package/dist/views/Steps/FlowChart/types.js.map +1 -0
  96. package/dist/views/Steps/StepModal.d.ts +9 -0
  97. package/dist/views/Steps/StepModal.d.ts.map +1 -0
  98. package/dist/views/Steps/StepModal.js +124 -0
  99. package/dist/views/Steps/StepModal.js.map +1 -0
  100. package/dist/views/Steps/StepsPanel.d.ts +6 -0
  101. package/dist/views/Steps/StepsPanel.d.ts.map +1 -0
  102. package/dist/views/{Tools/ToolsPanel.js → Steps/StepsPanel.js} +3 -3
  103. package/dist/views/{Tools/ToolsPanel.js.map → Steps/StepsPanel.js.map} +1 -1
  104. package/dist/views/{Tools → Steps}/dictionary.d.ts +9 -11
  105. package/dist/views/Steps/dictionary.d.ts.map +1 -0
  106. package/dist/views/{Tools → Steps}/dictionary.js +9 -11
  107. package/dist/views/Steps/dictionary.js.map +1 -0
  108. package/dist/views/{Tools → Steps}/index.d.ts +1 -1
  109. package/dist/views/Steps/index.d.ts.map +1 -0
  110. package/dist/views/{Tools → Steps}/index.js +10 -10
  111. package/dist/views/{Tools → Steps}/index.js.map +1 -1
  112. package/dist/views/Steps/utils.d.ts +6 -0
  113. package/dist/views/Steps/utils.d.ts.map +1 -0
  114. package/dist/views/{Tools → Steps}/utils.js.map +1 -1
  115. package/dist/views/Tools.d.ts +2 -0
  116. package/dist/views/Tools.d.ts.map +1 -0
  117. package/dist/views/Tools.js +51 -0
  118. package/dist/views/Tools.js.map +1 -0
  119. package/package.json +2 -2
  120. package/src/StackspotAIWidget.tsx +2 -0
  121. package/src/app-metadata.json +3 -3
  122. package/src/chat-interceptors/CustomInputs.ts +3 -3
  123. package/src/chat-interceptors/quick-commands.ts +2 -2
  124. package/src/chat-interceptors/send-message.ts +4 -2
  125. package/src/components/Markdown.tsx +1 -0
  126. package/src/components/StackedBadge.tsx +77 -0
  127. package/src/components/ToolBadge.tsx +146 -0
  128. package/src/index.ts +1 -1
  129. package/src/state/ChatEntry.ts +12 -20
  130. package/src/state/ChatState.ts +2 -2
  131. package/src/state/WidgetState.ts +4 -16
  132. package/src/state/types.ts +4 -1
  133. package/src/utils/agent.ts +1 -1
  134. package/src/utils/tools.ts +11 -0
  135. package/src/views/Agents/AgentDescription.tsx +14 -0
  136. package/src/views/Agents/AgentsTab.tsx +5 -10
  137. package/src/views/Agents/dictionary.ts +2 -0
  138. package/src/views/Agents/styled.ts +1 -1
  139. package/src/views/Chat/AgentInfo.tsx +6 -6
  140. package/src/views/Chat/ChatMessage.tsx +34 -4
  141. package/src/views/Chat/StepsList.tsx +6 -6
  142. package/src/views/Chat/styled.ts +21 -12
  143. package/src/views/ChatHistory/utils.ts +2 -2
  144. package/src/views/ChatTabSelection.tsx +22 -4
  145. package/src/views/MessageInput/AgentSelector.tsx +7 -9
  146. package/src/views/MessageInput/ButtonAgent.tsx +26 -2
  147. package/src/views/MessageInput/index.tsx +2 -2
  148. package/src/views/{Tools → Steps}/FlowChart/NodeStep.tsx +11 -23
  149. package/src/views/{Tools → Steps}/FlowChart/index.tsx +3 -7
  150. package/src/views/{Tools → Steps}/FlowChart/styled.ts +0 -49
  151. package/src/views/{Tools → Steps}/FlowChart/types.ts +3 -3
  152. package/src/views/Steps/StepModal.tsx +216 -0
  153. package/src/views/{Tools/ToolsPanel.tsx → Steps/StepsPanel.tsx} +2 -2
  154. package/src/views/{Tools → Steps}/dictionary.ts +9 -11
  155. package/src/views/{Tools → Steps}/index.tsx +10 -10
  156. package/src/views/{Tools → Steps}/utils.tsx +4 -4
  157. package/src/views/Tools.tsx +71 -0
  158. package/dist/views/Tools/FlowChart/NodeStep.js +0 -15
  159. package/dist/views/Tools/FlowChart/NodeStep.js.map +0 -1
  160. package/dist/views/Tools/FlowChart/index.d.ts +0 -9
  161. package/dist/views/Tools/FlowChart/index.d.ts.map +0 -1
  162. package/dist/views/Tools/FlowChart/types.d.ts.map +0 -1
  163. package/dist/views/Tools/FlowChart/types.js.map +0 -1
  164. package/dist/views/Tools/StepModal.d.ts +0 -9
  165. package/dist/views/Tools/StepModal.d.ts.map +0 -1
  166. package/dist/views/Tools/StepModal.js +0 -156
  167. package/dist/views/Tools/StepModal.js.map +0 -1
  168. package/dist/views/Tools/ToolsPanel.d.ts +0 -6
  169. package/dist/views/Tools/ToolsPanel.d.ts.map +0 -1
  170. package/dist/views/Tools/dictionary.d.ts.map +0 -1
  171. package/dist/views/Tools/dictionary.js.map +0 -1
  172. package/dist/views/Tools/index.d.ts.map +0 -1
  173. package/dist/views/Tools/utils.d.ts +0 -6
  174. package/dist/views/Tools/utils.d.ts.map +0 -1
  175. package/src/views/Tools/StepModal.tsx +0 -247
  176. /package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.d.ts +0 -0
  177. /package/dist/views/{Tools → Steps}/FlowChart/HandleGroup.js +0 -0
  178. /package/dist/views/{Tools → Steps}/FlowChart/NodeStep.d.ts +0 -0
  179. /package/dist/views/{Tools → Steps}/FlowChart/layout.d.ts +0 -0
  180. /package/dist/views/{Tools → Steps}/FlowChart/layout.js +0 -0
  181. /package/dist/views/{Tools → Steps}/FlowChart/styled.d.ts +0 -0
  182. /package/dist/views/{Tools → Steps}/FlowChart/types.js +0 -0
  183. /package/dist/views/{Tools → Steps}/utils.js +0 -0
  184. /package/src/views/{Tools → Steps}/FlowChart/HandleGroup.tsx +0 -0
  185. /package/src/views/{Tools → Steps}/FlowChart/layout.ts +0 -0
package/src/index.ts CHANGED
@@ -11,5 +11,5 @@ export { ChatState } from './state/ChatState'
11
11
  export type { MessageInterceptor } from './state/ChatState'
12
12
  export { ChatTabsController } from './state/ChatTabsController'
13
13
  export { ObservableState } from './state/ObservableState'
14
- export type { Labeled, LabeledWithImage } from './state/types'
14
+ export type { Labeled, LabeledAgent } from './state/types'
15
15
  export { WidgetState } from './state/WidgetState'
@@ -1,7 +1,8 @@
1
1
  import { OneOfColorSchemes } from '@citric/core'
2
+ import { ChatStep } from '@stack-spot/portal-network'
2
3
  import { ColorPaletteName } from '@stack-spot/portal-theme'
3
4
  import { pull } from 'lodash'
4
- import { LabeledWithImage } from './types'
5
+ import { LabeledAgent } from './types'
5
6
 
6
7
  export interface ActionDataClick {
7
8
  name?: string,
@@ -57,24 +58,11 @@ export interface KnowledgeSource {
57
58
  documentId: string,
58
59
  }
59
60
 
60
- export interface AgentTool {
61
+ export interface ChatTool {
61
62
  id: string,
62
- name: string,
63
- description?: string,
64
63
  image?: string,
65
- duration?: number,
66
- input?: string,
67
- output?: string,
68
- }
69
-
70
- export interface ChatEntryStep {
71
- id: string,
72
- type: 'planning' | 'step' | 'answer',
73
- input?: string,
74
- output?: string,
75
- status: 'pending' | 'running' | 'success' | 'error',
76
- duration?: number,
77
- tools?: AgentTool[],
64
+ name: string,
65
+ description: string,
78
66
  }
79
67
 
80
68
  export interface TextChatEntry {
@@ -109,9 +97,9 @@ export interface TextChatEntry {
109
97
  */
110
98
  updated?: string,
111
99
  /**
112
- * The agent who wrote the message. Undefined for the default agent or if the agentType is "user".
100
+ * The agent who wrote the message. Undefined for the agentType is "user".
113
101
  */
114
- agent?: LabeledWithImage,
102
+ agent?: LabeledAgent,
115
103
  /**
116
104
  * The unique identifier of the message.
117
105
  */
@@ -131,7 +119,11 @@ export interface TextChatEntry {
131
119
  /**
132
120
  * This entry may contain steps. If so, specify them in this array.
133
121
  */
134
- steps?: ChatEntryStep[],
122
+ steps?: ChatStep[],
123
+ /**
124
+ * If any tool was used to generate the response, its id is returned in this list.
125
+ */
126
+ tools?: string[],
135
127
  /*
136
128
  * Options for radio, checkbox or button type.
137
129
  */
@@ -4,7 +4,7 @@ import { AbortedError } from '../AbortedError'
4
4
  import { ChatFeatures, getFeaturesWithDefaults } from '../features'
5
5
  import { ChatEntry } from './ChatEntry'
6
6
  import { ObservableState } from './ObservableState'
7
- import { Labeled, LabeledWithImage } from './types'
7
+ import { Labeled, LabeledAgent } from './types'
8
8
 
9
9
  export interface ChatPropertiesWithOptionalFeatures {
10
10
  /**
@@ -14,7 +14,7 @@ export interface ChatPropertiesWithOptionalFeatures {
14
14
  /**
15
15
  * The current AI agent.
16
16
  */
17
- agent?: LabeledWithImage & { builtIn: boolean, slug?: string },
17
+ agent?: LabeledAgent,
18
18
  /**
19
19
  * The current workspace.
20
20
  */
@@ -13,15 +13,15 @@ export interface WidgetProperties {
13
13
  /**
14
14
  * Current content of the right panel. Undefined for closed right panel.
15
15
  */
16
- panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details' | 'tools',
16
+ panel?: 'stack' | 'workspace' | 'agent' | 'ks' | 'editor' | 'history' | 'ks-details' | 'steps' | 'tools',
17
17
  /**
18
18
  * KS to use when the right panel "ks-details" is open.
19
19
  */
20
20
  currentKSInPanel?: { name: string, slug: string, score: number, documentId: string },
21
21
  /**
22
- * The message to show in the tools panel.
22
+ * The message to show in the tools panel or the steps panel.
23
23
  */
24
- currentMessageInToolsPanel?: { chatId: string, messageId: number },
24
+ currentMessageInPanel?: { chatId: string, messageId: number },
25
25
  /**
26
26
  * Whether or not the widget is in its minimized version.
27
27
  */
@@ -58,8 +58,6 @@ interface Options {
58
58
  chatTabs?: ChatTabsController,
59
59
  }
60
60
 
61
- const untitledChatPrefix = 'Chat '
62
-
63
61
  /**
64
62
  * Holds the full state of the AI Chat Widget.
65
63
  *
@@ -90,16 +88,6 @@ export class WidgetState extends ObservableState<WidgetProperties> {
90
88
  this.createChat()
91
89
  }
92
90
 
93
- private getNextUntitledChatIndex() {
94
- let max = 0
95
- for (const chat of this.chatTabs.getAll()) {
96
- const [, match] = chat.get('label').match(`${untitledChatPrefix}(\\d+)`) ?? []
97
- const index = parseInt(match)
98
- if (index > max) max = index
99
- }
100
- return max + 1
101
- }
102
-
103
91
  /**
104
92
  * Utility function for adding a new chat to the chat tabs. This also selects the new tab.
105
93
  * @param properties the chat properties (initial state).
@@ -109,7 +97,7 @@ export class WidgetState extends ObservableState<WidgetProperties> {
109
97
  createChat({ label, ...properties }: Partial<ChatPropertiesWithOptionalFeatures> = {}, entries: ChatEntry[] = []) {
110
98
  const chat = new ChatState({
111
99
  id: ulid(),
112
- initial: { label: label || `${untitledChatPrefix}${this.getNextUntitledChatIndex()}`, features: this.chatFeatures, ...properties },
100
+ initial: { label: label || 'Chat', features: this.chatFeatures, ...properties },
113
101
  entries,
114
102
  interceptors: this.interceptors,
115
103
  untitled: !label,
@@ -1,8 +1,11 @@
1
+
1
2
  export interface Labeled {
2
3
  id: string,
3
4
  label: string,
4
5
  }
5
6
 
6
- export interface LabeledWithImage extends Labeled {
7
+ export interface LabeledAgent extends Labeled {
7
8
  image?: string,
9
+ builtIn?: boolean,
10
+ slug?: string,
8
11
  }
@@ -1 +1 @@
1
- export const isAgentDefault = (agentSlug?: string) => agentSlug === 'stk_code_buddy'
1
+ export const isAgentDefault = (agentSlug?: string) => agentSlug === 'stk_code_buddy'
@@ -0,0 +1,11 @@
1
+ import { BuiltinToolResponse, ToolkitsInAgentResponse } from '@stack-spot/portal-network/api/agent'
2
+
3
+ export type ToolWithImage = BuiltinToolResponse & { id: string, image?: string }
4
+
5
+ export function toolById(id: string, toolkits: ToolkitsInAgentResponse | undefined): ToolWithImage | undefined {
6
+ for (const toolkit of toolkits?.builtins ?? []) {
7
+ for (const tool of toolkit.tools ?? []) {
8
+ if (tool.id === id) return { ...tool, id, image: toolkit.image_url }
9
+ }
10
+ }
11
+ }
@@ -2,6 +2,7 @@ import { Text } from '@citric/core'
2
2
  import { Badge, Skeleton } from '@citric/ui'
3
3
  import { agentClient } from '@stack-spot/portal-network'
4
4
  import { useMemo } from 'react'
5
+ import { ToolBadge } from '../../components/ToolBadge'
5
6
  import { useAgentsDictionary } from './dictionary'
6
7
  import { AgentDescriptionBox } from './styled'
7
8
 
@@ -28,6 +29,15 @@ export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeS
28
29
  }
29
30
  return loadingKS
30
31
  }, [numberOfKnowledgeSources])
32
+ const tools = useMemo(() => {
33
+ const result: React.ReactElement[] = []
34
+ for (const kit of agent?.toolkits?.builtins ?? []) {
35
+ for (const tool of kit.tools ?? []) {
36
+ result.push(<li key={tool.id}><ToolBadge name={tool.name || tool.id || 'unknown'} image={kit.image_url} /></li>)
37
+ }
38
+ }
39
+ return result
40
+ }, [agent])
31
41
 
32
42
  return (
33
43
  <AgentDescriptionBox>
@@ -39,6 +49,10 @@ export const AgentDescription = ({ agentId, llm, description, numberOfKnowledgeS
39
49
  <Text appearance="microtext1" className="title">Knowledge sources</Text>
40
50
  <ul>{isLoading ? skeleton : knowledgeSources}</ul>
41
51
  </section>}
52
+ {!!tools.length && <section>
53
+ <Text appearance="microtext1" className="title">{t.tools}</Text>
54
+ <ul>{tools}</ul>
55
+ </section>}
42
56
  {llm && <section>
43
57
  <Text appearance="microtext1" className="title">LLM</Text>
44
58
  <Badge palette="orange" appearance="square">{llm}</Badge>
@@ -20,24 +20,19 @@ export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT
20
20
  const chat = useCurrentChat()
21
21
  const [filter, setFilter] = useState('')
22
22
  const agentsBuiltIn = agentClient.publicAgents.useQuery({})
23
- const defaultAgent = agentsBuiltIn.find((agent) => isAgentDefault(agent.slug))
23
+ const agentDefault = agentsBuiltIn.find((agent) => isAgentDefault(agent.slug))
24
24
  const agents = visibility === 'BUILT-IN' ? agentsBuiltIn : agentClient.agents.useQuery({ visibility })
25
25
  const [value, setValue] = useState<AgentResponse | undefined>(
26
- chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : defaultAgent,
26
+ chat.get('agent') ? agents.find(a => a.id === chat.get('agent')?.id) : agentDefault,
27
27
  )
28
28
  const filtered = useMemo(
29
- () => {
30
- return filter ? agents.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : agents
31
- },
29
+ () => filter ? agents.filter(a => a === value || a.name.toLocaleLowerCase().includes(filter.toLocaleLowerCase())) : agents,
32
30
  [agents, filter, value],
33
31
  )
34
32
 
35
33
  function submit() {
36
34
  if (value) {
37
- chat.set('agent', !isAgentDefault(value.slug)
38
- ? { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN', slug: value.slug }
39
- : undefined
40
- )
35
+ chat.set('agent', { id: value.id, label: value.name, image: value.avatar, builtIn: visibility === 'BUILT-IN', slug: value.slug })
41
36
  }
42
37
  close()
43
38
  }
@@ -53,7 +48,7 @@ export const AgentsTab = ({ visibility }: { visibility: VisibilityLevel | 'BUILT
53
48
  onChange={setValue}
54
49
  renderLabel={({ name, avatar, id }) => (
55
50
  <AgentLabel>
56
- {id ? (avatar ? <img src={avatar} /> : <IconBox size="xs"><Agent /></IconBox>) : <MiniLogo />}
51
+ {id ? (avatar ? <img src={avatar} /> : <IconBox size="xs"><Agent /></IconBox>) : <MiniLogo />}
57
52
  <Text>{name}</Text>
58
53
  </AgentLabel>
59
54
  )}
@@ -15,6 +15,7 @@ const dictionary = {
15
15
  noDataDescription: 'Use the tabs above to try other categories or use the AI portal to create new agents.',
16
16
  defaultAgentDescription: 'The StackSpot CodeGen is an advanced artificial intelligence agent designed to optimize and accelerate software development. Integrated directly into your integrated development environment, StackSpot CodeGen offers real-time code suggestions, helping developers write high-quality code more efficiently. With robust features such as creating Stacks AI, customized knowledge sources, and quick commands, StackSpot CodeGen contextualizes your development needs to provide the best answers and code suggestions.',
17
17
  description: 'Description',
18
+ tools: 'Tools',
18
19
  },
19
20
  pt: {
20
21
  title: 'Agentes',
@@ -30,6 +31,7 @@ const dictionary = {
30
31
  noDataDescription: 'Use as abas acima para tentar outras categorias ou use o Portal AI para criar novos agentes.',
31
32
  defaultAgentDescription: 'O StackSpot CodeGen é um agente de inteligência artificial avançado projetado para otimizar e acelerar o desenvolvimento de software. Integrado diretamente ao seu ambiente de desenvolvimento, o StackSpot CodeGen oferece sugestões de código em tempo real, ajudando os desenvolvedores a escreverem código de alta qualidade de forma mais eficiente. Com recursos robustos, como a criação de Stacks AI, Knowledge Sources personalizadas e comandos rápidos, o StackSpot CodeGen contextualiza suas necessidades de desenvolvimento para fornecer as melhores respostas e sugestões de código.',
32
33
  description: 'Descrição',
34
+ tools: 'Ferramentas',
33
35
  },
34
36
  } satisfies Dictionary
35
37
 
@@ -30,7 +30,7 @@ export const AgentDescriptionBox = styled.div`
30
30
  border-bottom: none;
31
31
  }
32
32
 
33
- .title {
33
+ > .title {
34
34
  display: block;
35
35
  margin-bottom: 6px;
36
36
  }
@@ -1,9 +1,9 @@
1
- import { Text } from '@citric/core'
2
- import { MiniLogo } from '@stack-spot/portal-components/svg'
3
- import { LabeledWithImage } from '../../state/types'
1
+ import { IconBox, Text } from '@citric/core'
2
+ import { Agent } from '@citric/icons'
3
+ import { LabeledAgent } from '../../state/types'
4
4
 
5
5
  interface Props {
6
- agent?: LabeledWithImage,
6
+ agent?: LabeledAgent,
7
7
  }
8
8
 
9
9
  /**
@@ -13,8 +13,8 @@ export const AgentInfo = ({ agent }: Props) => (
13
13
  <>
14
14
  {agent?.image
15
15
  ? <img src={agent.image} className="custom-agent-image" />
16
- : <div className="default-image-wrapper"><MiniLogo className="agent-image" /></div>
16
+ : <IconBox className="default-image-wrapper" colorIcon="light.700"><Agent className="agent-image" /></IconBox>
17
17
  }
18
- <Text appearance="body2">{agent?.label || 'StackSpot AI CodeGen'}</Text>
18
+ <Text appearance="body2">{agent?.label}</Text>
19
19
  </>
20
20
  )
@@ -1,7 +1,8 @@
1
1
  /* eslint-disable semi */
2
2
  import { Box, Button, Checkbox, Flex, IconBox, Input, Label, Radio, Text } from '@citric/core'
3
- import { Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
3
+ import { Cog, Copy, Dislike, DislikeFill, Like, LikeFill, TimesCircle } from '@citric/icons'
4
4
  import { Avatar, Badge, IconButton } from '@citric/ui'
5
+ import { agentClient } from '@stack-spot/portal-network'
5
6
  import { listToClass } from '@stack-spot/portal-theme'
6
7
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
7
8
  import { debounce } from 'lodash'
@@ -9,9 +10,11 @@ import { Dispatch, useCallback, useEffect, useMemo, useRef, useState } from 'rea
9
10
  import { PhoneInput } from 'react-international-phone'
10
11
  import 'react-international-phone/style.css'
11
12
  import { Markdown } from '../../components/Markdown'
13
+ import { StackedBadge } from '../../components/StackedBadge'
12
14
  import { useChatEntry, useCurrentChat, useWidget } from '../../context/hooks'
13
15
  import { ChatEntry, SerializableAction, TextChatEntry } from '../../state/ChatEntry'
14
16
  import { useDateFormatter } from '../../utils/date'
17
+ import { toolById } from '../../utils/tools'
15
18
  import { AgentInfo } from './AgentInfo'
16
19
  import { useChatScrollToBottomEffect } from './chat-scroll'
17
20
  import { onCopyAll, onCopyCode, onLikeOrDislike } from './events'
@@ -82,7 +85,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
82
85
  {option.hasInput && option.value && labels.findIndex((label) => label === option.value) !== -1 &&
83
86
  <Box w={4} ml={2}>
84
87
  <Input name={entry.name} onChange={(data) => setValue([data.target.value])}
85
- required={true} />
88
+ required={true} sx={{ height: '30px' }} />
86
89
  </Box>
87
90
  }
88
91
  </Flex>
@@ -120,7 +123,7 @@ const RenderInputsEntry = ({ isLast, entry, value, setValue, labels, setLabels }
120
123
  {option.hasInput && option.value && labels.findIndex((label) => label === option.value)!== -1 &&
121
124
  <Box w={4} ml={2}>
122
125
  <Input name={entry.name} {...entry.validations} onChange={(data) => handleChange(data)}
123
- required={true} />
126
+ required={true} sx={{ height: '30px' }} />
124
127
  </Box>
125
128
  }
126
129
  </Flex>
@@ -163,6 +166,11 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
163
166
  const chatRef = useRef<HTMLDivElement>(null)
164
167
  const widget = useWidget()
165
168
  const chat = useCurrentChat()
169
+ const agentId = entry.agent?.id ?? ''
170
+ const [agentById] = agentClient.agent.useStatefulQuery({ agentId }, { enabled: !!agentId && !entry?.agent?.builtIn })
171
+ const [AgentBuiltInById] = agentClient.publicAgent.useStatefulQuery({ agentId }, { enabled: !!agentId && entry?.agent?.builtIn })
172
+ const agent = agentById ?? AgentBuiltInById
173
+
166
174
  useChatScrollToBottomEffect(ref, [entry])
167
175
 
168
176
  const detailKS = useCallback(({ name, slug, documentScore, documentId }: Required<TextChatEntry>['knowledgeSources'][number]) => {
@@ -249,10 +257,15 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
249
257
  </form>
250
258
  }
251
259
 
260
+ function openToolsPanel() {
261
+ widget.set('currentMessageInPanel', { chatId: chat.id, messageId: message.id })
262
+ widget.set('panel', 'tools')
263
+ }
264
+
252
265
  return (entry.content || entry.error || !!entry.steps?.length) && (
253
266
  <li className={entry.agentType} ref={ref}>
254
267
  <div className="chat-message" ref={chatRef} onKeyDown={handleKeyDown} tabIndex={0}>
255
- <div className="user-info">{userInfo}</div>
268
+ <div className={`user-info ${entry.agentType}`}>{userInfo}</div>
256
269
  {(entry.content || entry.steps) && <div className={listToClass(['message-content', entry.card && 'card'])}>
257
270
  {!!entry.badges?.length && <div className="badges">
258
271
  {entry.badges.map((b, index) => <Badge key={index} palette={b.color ?? 'cyan'} appearance="square">{b.label}</Badge>)}
@@ -268,6 +281,19 @@ export const ChatMessage = ({ message, username, isLast }: Props) => {
268
281
  <Text appearance="microtext1">{entry.error}</Text>
269
282
  </div>
270
283
  )}
284
+ {!!entry.tools?.length && <StackedBadge
285
+ aria-label={t.openToolsPanel}
286
+ title={t.openToolsPanel}
287
+ tabIndex={0}
288
+ role="button"
289
+ className="tools-badge"
290
+ label={t.tools}
291
+ images={entry.tools.slice(0, 3).map((id) => {
292
+ const tool = toolById(id, agent?.toolkits)
293
+ return { key: id, name: tool?.name || id, icon: <Cog />, url: tool?.image }
294
+ })}
295
+ onClick={openToolsPanel}
296
+ />}
271
297
  {!!entry.knowledgeSources?.length && <div className="ks-box">
272
298
  <Text appearance="microtext1" colorScheme="light.700">Knowledge Sources:</Text>
273
299
  <ul>{entry.knowledgeSources.map((ks, index) => (
@@ -307,10 +333,14 @@ const dictionary = {
307
333
  copy: 'Copy',
308
334
  like: 'Like',
309
335
  dislike: 'Dislike',
336
+ tools: 'Tools',
337
+ openToolsPanel: 'Open the tools panel to see more details.',
310
338
  },
311
339
  pt: {
312
340
  copy: 'Copiar',
313
341
  like: 'Gostei',
314
342
  dislike: 'Não gostei',
343
+ tools: 'Ferramentas',
344
+ openToolsPanel: 'Abrir o painel de ferramentas para ver mais detalhes.',
315
345
  },
316
346
  } satisfies Dictionary
@@ -2,27 +2,27 @@ import { Button, IconBox, Text } from '@citric/core'
2
2
  import { CheckCircleFill, Circle, PlayFill, TimesCircleFill } from '@citric/icons'
3
3
  import { LoadingCircular } from '@citric/ui'
4
4
  import { AnimatedHeight } from '@stack-spot/portal-components/AnimatedHeight'
5
+ import { ChatStep, StepChatStep } from '@stack-spot/portal-network'
5
6
  import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
6
7
  import { findLastIndex } from 'lodash'
7
8
  import { useState } from 'react'
8
9
  import { useWidget } from '../../context/hooks'
9
- import { ChatEntryStep } from '../../state/ChatEntry'
10
10
  import { PropsOf } from '../../types'
11
11
 
12
12
  interface Props {
13
- steps: ChatEntryStep[],
13
+ steps: ChatStep[],
14
14
  messageId: number,
15
15
  chatId: string,
16
16
  }
17
17
 
18
18
  interface StepProps {
19
- step: ChatEntryStep,
19
+ step: StepChatStep,
20
20
  index: number,
21
21
  total: number,
22
22
  onClick?: () => void,
23
23
  }
24
24
 
25
- function getStatusIcon(status: ChatEntryStep['status']) {
25
+ function getStatusIcon(status: ChatStep['status']) {
26
26
  const iconBoxProps: PropsOf<typeof IconBox> = { colorIcon: 'light.700', size: 'xs' }
27
27
  switch (status) {
28
28
  case 'error': return <IconBox {...iconBoxProps}><TimesCircleFill /></IconBox>
@@ -53,8 +53,8 @@ export const StepsList = ({ steps, chatId, messageId }: Props) => {
53
53
  const widget = useWidget()
54
54
 
55
55
  function openToolsPanel() {
56
- widget.set('currentMessageInToolsPanel', { chatId, messageId })
57
- widget.set('panel', 'tools')
56
+ widget.set('currentMessageInPanel', { chatId, messageId })
57
+ widget.set('panel', 'steps')
58
58
  }
59
59
 
60
60
  return (
@@ -38,6 +38,10 @@ export const ChatList: IStyledComponentBase<
38
38
  display: flex;
39
39
  flex-direction: row;
40
40
  gap: 10px;
41
+
42
+ &.bot {
43
+ align-items: center;
44
+ }
41
45
  }
42
46
 
43
47
  .chat-message {
@@ -82,23 +86,19 @@ export const ChatList: IStyledComponentBase<
82
86
  gap: 4px;
83
87
 
84
88
  .default-image-wrapper {
85
- width: 24px;
86
- height: 24px;
87
- border-radius: 4px;
89
+ width: 32px;
90
+ height: 32px;
91
+ border-radius: 50%;
88
92
  background-color: ${theme.color.light[300]};
89
93
  display: flex;
90
94
  align-items: center;
91
95
  justify-content: center;
92
-
93
- .agent-image {
94
- width: 18px;
95
- height: 18px;
96
- }
96
+ border: 1px solid ${theme.color.light[600]};
97
97
  }
98
98
 
99
99
  .custom-agent-image {
100
- width: 24px;
101
- height: 24px;
100
+ width: 32px;
101
+ height: 32px;
102
102
  border-radius: 50%;
103
103
  }
104
104
  }
@@ -149,11 +149,11 @@ export const ChatList: IStyledComponentBase<
149
149
  background-color: ${theme.color.light[500]};
150
150
  border-radius: 4px;
151
151
 
152
- > p:first-child {
152
+ .markdown > p:first-child {
153
153
  margin-top: 0;
154
154
  }
155
155
 
156
- > p:last-child {
156
+ .markdown > p:last-child {
157
157
  margin-bottom: 0;
158
158
  }
159
159
  }
@@ -244,4 +244,13 @@ export const ChatList: IStyledComponentBase<
244
244
  }
245
245
  }
246
246
  }
247
+
248
+ .markdown img {
249
+ max-width: 70%;
250
+ }
251
+
252
+ .tools-badge {
253
+ align-self: start;
254
+ cursor: pointer;
255
+ }
247
256
  `
@@ -1,6 +1,6 @@
1
1
  import { agentClient, aiClient, workspaceClient } from '@stack-spot/portal-network'
2
2
  import { ChatProperties } from '../../state/ChatState'
3
- import { LabeledWithImage } from '../../state/types'
3
+ import { LabeledAgent } from '../../state/types'
4
4
 
5
5
  /**
6
6
  * Finds a stack by its id.
@@ -40,7 +40,7 @@ export async function findWorkspace(id: string | null): Promise<ChatProperties['
40
40
  * Finds all the agents, including common agents and public agents.
41
41
  * @returns an array with every agent.
42
42
  */
43
- export async function getAllAgents(): Promise<LabeledWithImage[]> {
43
+ export async function getAllAgents(): Promise<LabeledAgent[]> {
44
44
  try {
45
45
  const [agents, publicAgents] = await Promise.all([agentClient.agents.query({}), agentClient.publicAgents.query({})])
46
46
  return [...agents, ...publicAgents].map(a => ({ id: a.id, label: a.name, image: a.avatar }))
@@ -3,6 +3,8 @@ import { Dictionary, useTranslate } from '@stack-spot/portal-translate'
3
3
  import { useMemo } from 'react'
4
4
  import { TabManager } from '../components/TabManager'
5
5
  import { useChatState, useChatTabs, useWidget, useWidgetState } from '../context/hooks'
6
+ import { useRightPanel } from '../right-panel/hooks'
7
+ import { ChatState } from '../state/ChatState'
6
8
  import { ButtonAction } from '../types'
7
9
 
8
10
  const TabLabel = ({ id }: { id: string }) => {
@@ -18,13 +20,29 @@ export const ChatTabSelection = () => {
18
20
  const widget = useWidget()
19
21
  const { chatHistory } = useWidgetState('features') ?? {}
20
22
  const { active, chats } = useChatTabs()
23
+ const { close: closeRightPanel } = useRightPanel()
24
+
25
+ function create() {
26
+ widget.createChat()
27
+ closeRightPanel()
28
+ }
29
+
30
+ function select({ id }: ChatState) {
31
+ widget.chatTabs.select(id)
32
+ closeRightPanel()
33
+ }
34
+
35
+ function remove({ id }: ChatState) {
36
+ widget.chatTabs.remove(id)
37
+ closeRightPanel()
38
+ }
21
39
 
22
40
  const buttons = useMemo<ButtonAction[]>(
23
41
  () => {
24
42
  const actions: ButtonAction[] = [{
25
43
  icon: <Plus />,
26
44
  label: t.newChat,
27
- onClick: () => widget.createChat(),
45
+ onClick: create,
28
46
  }]
29
47
  if (chatHistory) {
30
48
  actions.push({
@@ -39,14 +57,14 @@ export const ChatTabSelection = () => {
39
57
  },
40
58
  [chatHistory],
41
59
  )
42
-
60
+
43
61
  return <TabManager
44
62
  tabs={chats}
45
63
  active={active.id}
46
64
  renderLabel={({ id }) => <TabLabel id={id} />}
47
65
  keygen={({ id }) => id}
48
- onRemove={({ id }) => widget.chatTabs.remove(id)}
49
- onSelect={({ id }) => widget.chatTabs.select(id)}
66
+ onRemove={remove}
67
+ onSelect={select}
50
68
  buttons={buttons}
51
69
  />
52
70
  }
@@ -1,6 +1,5 @@
1
1
  import { Flex, IconBox, Image } from '@citric/core'
2
2
  import { Agent } from '@citric/icons'
3
- import { MiniLogo } from '@stack-spot/portal-components/svg'
4
3
  import { agentClient } from '@stack-spot/portal-network'
5
4
  import { AgentResponse } from '@stack-spot/portal-network/api/agent'
6
5
  import { uniqBy } from 'lodash'
@@ -8,7 +7,6 @@ import { useCallback } from 'react'
8
7
  import { Selector } from '../../components/Selector'
9
8
  import { useCurrentChat, useCurrentChatState } from '../../context/hooks'
10
9
  import { agentRegex } from '../../regex'
11
- import { isAgentDefault } from '../../utils/agent'
12
10
 
13
11
  export const AgentSelector = ({ inputRef }: { inputRef: React.RefObject<HTMLTextAreaElement | HTMLInputElement> }) => {
14
12
  const chat = useCurrentChat()
@@ -16,9 +14,9 @@ export const AgentSelector = ({ inputRef }: { inputRef: React.RefObject<HTMLText
16
14
  const onSelectItem = useCallback((agent: AgentResponse) => {
17
15
  const newValue = `@${agent.slug}`
18
16
  chat.set('nextMessage', undefined)
19
- chat.set('agent', isAgentDefault(agent.slug)
20
- ? undefined
21
- : { id: agent.id, label: agent.name, image: agent.avatar, builtIn: agent.visibility_level === 'builtIn' },
17
+ chat.set(
18
+ 'agent',
19
+ { id: agent.id, label: agent.name, image: agent.avatar, slug: agent.slug, builtIn: agent.visibility_level === 'builtIn' },
22
20
  )
23
21
 
24
22
  if (!inputRef.current) return
@@ -31,16 +29,16 @@ export const AgentSelector = ({ inputRef }: { inputRef: React.RefObject<HTMLText
31
29
  const accountAgents = agentClient.agents.useQuery({ visibility: 'ACCOUNT' })
32
30
  const sharedAgents = agentClient.agents.useQuery({ visibility: 'SHARED' })
33
31
  const publicAgents = agentClient.publicAgents.useQuery({})
34
- const buildIns = [...publicAgents.map((agent) => ({ ...agent, visibility_level: 'builtIn' }))]
32
+ const builtIns = [...publicAgents.map((agent) => ({ ...agent, visibility_level: 'builtIn' }))]
35
33
 
36
- return uniqBy([...personalAgents, ...accountAgents, ...sharedAgents, ...buildIns], 'id')
34
+ return uniqBy([...personalAgents, ...accountAgents, ...sharedAgents, ...builtIns], 'id')
37
35
  }
38
36
 
39
- const AgentItem = ({ avatar, name, slug }: AgentResponse) => {
37
+ const AgentItem = ({ avatar, name }: AgentResponse) => {
40
38
  const AvatarComponent = avatar ? <Image width="32" height="32" radius="full" src={avatar} /> : <IconBox size="md"><Agent /></IconBox>
41
39
 
42
40
  return <Flex flexWrap="nowrap" alignItems="center" sx={{ gap: '8px' }}>
43
- {isAgentDefault(slug) ? <MiniLogo style={{ width: '32px', height: '32px' }} /> : AvatarComponent}
41
+ {AvatarComponent}
44
42
  <p className="selector-title">{name}</p>
45
43
  </Flex>
46
44
  }