@vibe-forge/client 0.7.4 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/cli.cjs +16 -17
  2. package/dist/assets/{arc-DXs6SvQX.js → arc-BjI8Mzf5.js} +1 -1
  3. package/dist/assets/{blockDiagram-c4efeb88-h-xVkbzT.js → blockDiagram-c4efeb88-By4JL1RU.js} +1 -1
  4. package/dist/assets/{c4Diagram-c83219d4-DEumwLCr.js → c4Diagram-c83219d4-frpxdNO6.js} +1 -1
  5. package/dist/assets/channel-Da5T54-_.js +1 -0
  6. package/dist/assets/{classDiagram-beda092f-Dh_6VL8e.js → classDiagram-beda092f-sGBIwOiO.js} +1 -1
  7. package/dist/assets/{classDiagram-v2-2358418a-D9hG_V5y.js → classDiagram-v2-2358418a-JfASkQqT.js} +1 -1
  8. package/dist/assets/clone-BfjbcwWs.js +1 -0
  9. package/dist/assets/{createText-1719965b-DGO5tdKk.js → createText-1719965b-C6SwHZ-r.js} +1 -1
  10. package/dist/assets/{edges-96097737-63FzeZDk.js → edges-96097737-z-Dp4FKF.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-jN2RzBTN.js → erDiagram-0228fc6a-5fIyCTtw.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-CvND0Kz-.js → flowDb-c6c81e3f-DuDOLffh.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-jtMtLi5z.js → flowDiagram-50d868cf-CqfJoZYm.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-B25RT9lb.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-Dic1wweO.js → flowchart-elk-definition-6af322e1-wpZusdN_.js} +1 -1
  16. package/dist/assets/{ganttDiagram-a2739b55-BLbYj7ru.js → ganttDiagram-a2739b55-aw70jAEI.js} +1 -1
  17. package/dist/assets/{gitGraphDiagram-82fe8481-Dm4ee53U.js → gitGraphDiagram-82fe8481-DhJVtfJF.js} +1 -1
  18. package/dist/assets/{graph-BnzAin3i.js → graph-Dp5XlF1F.js} +1 -1
  19. package/dist/assets/{index-5325376f-gU7GGRnq.js → index-5325376f-C7cRw1io.js} +1 -1
  20. package/dist/assets/{index-BRIfON-w.css → index-DHL1Qu5o.css} +1 -1
  21. package/dist/assets/index-DqioMim6.js +557 -0
  22. package/dist/assets/{infoDiagram-8eee0895-BI_1UH70.js → infoDiagram-8eee0895-B9VmKQm_.js} +1 -1
  23. package/dist/assets/{journeyDiagram-c64418c1-Xc6td0Nk.js → journeyDiagram-c64418c1-BTKwOAU-.js} +1 -1
  24. package/dist/assets/{layout-PHWoi3a3.js → layout-XtAsDaFY.js} +1 -1
  25. package/dist/assets/{line-BJPgSD92.js → line-1nd8Xc89.js} +1 -1
  26. package/dist/assets/{linear-DYKGy-mG.js → linear-BBztVBp6.js} +1 -1
  27. package/dist/assets/{mermaid.core-H3QJi-7A.js → mermaid.core-DaqQ11eY.js} +4 -4
  28. package/dist/assets/{mindmap-definition-8da855dc-UC--JAZa.js → mindmap-definition-8da855dc-DYdtyQbX.js} +1 -1
  29. package/dist/assets/{pieDiagram-a8764435-BTI_-cYX.js → pieDiagram-a8764435-CO9FnqSm.js} +1 -1
  30. package/dist/assets/{quadrantDiagram-1e28029f-C4Gf_SaX.js → quadrantDiagram-1e28029f-Cs-iTCZ-.js} +1 -1
  31. package/dist/assets/{requirementDiagram-08caed73-BKwfGAsO.js → requirementDiagram-08caed73-Diwrdq_y.js} +1 -1
  32. package/dist/assets/{sankeyDiagram-a04cb91d-DTp2p2pD.js → sankeyDiagram-a04cb91d-DjxNZwMs.js} +1 -1
  33. package/dist/assets/{sequenceDiagram-c5b8d532-CLuNEegU.js → sequenceDiagram-c5b8d532-CWawhoyM.js} +1 -1
  34. package/dist/assets/{stateDiagram-1ecb1508-BUofUUM6.js → stateDiagram-1ecb1508-Bow7IRrW.js} +1 -1
  35. package/dist/assets/{stateDiagram-v2-c2b004d7-BATuZH_y.js → stateDiagram-v2-c2b004d7-BJqu9_Fj.js} +1 -1
  36. package/dist/assets/{styles-b4e223ce-CVO41uVV.js → styles-b4e223ce-F2FDTYdm.js} +1 -1
  37. package/dist/assets/{styles-ca3715f6-fFE_-gsH.js → styles-ca3715f6-DJITgKSs.js} +1 -1
  38. package/dist/assets/{styles-d45a18b0-BeG4Dd2L.js → styles-d45a18b0-DMSpafXP.js} +1 -1
  39. package/dist/assets/{svgDrawCommon-b86b1483-D6PZVIuy.js → svgDrawCommon-b86b1483-3_yd3bB_.js} +1 -1
  40. package/dist/assets/{timeline-definition-faaaa080-CTFMc2GO.js → timeline-definition-faaaa080-CV5umgp5.js} +1 -1
  41. package/dist/assets/{xychartDiagram-f5964ef8-wWcw3yKn.js → xychartDiagram-f5964ef8-DhVTgtev.js} +1 -1
  42. package/dist/index.html +2 -2
  43. package/package.json +10 -8
  44. package/src/App.tsx +1 -1
  45. package/src/api/base.ts +7 -7
  46. package/src/api/benchmark.ts +7 -3
  47. package/src/api/config.ts +2 -1
  48. package/src/api.ts +1 -1
  49. package/src/components/ArchiveView.tsx +1 -1
  50. package/src/components/ConfigView.tsx +18 -6
  51. package/src/components/MarkdownContent.tsx +1 -1
  52. package/src/components/Sidebar.tsx +2 -2
  53. package/src/components/automation-view/RuleFormPanel.tsx +7 -5
  54. package/src/components/automation-view/TaskList.tsx +8 -5
  55. package/src/components/automation-view/TriggerList.tsx +25 -15
  56. package/src/components/automation-view/types.ts +1 -1
  57. package/src/components/benchmark-view/BenchmarkCasePanel.tsx +94 -94
  58. package/src/components/benchmark-view/BenchmarkSidebar.scss +8 -6
  59. package/src/components/benchmark-view/BenchmarkSidebar.tsx +43 -30
  60. package/src/components/benchmark-view/index.tsx +4 -2
  61. package/src/components/benchmark-view/types.ts +3 -2
  62. package/src/components/benchmark-view/utils.ts +1 -2
  63. package/src/components/chat/ChatHeader.tsx +1 -1
  64. package/src/components/chat/ChatHistoryView.tsx +3 -2
  65. package/src/components/chat/CurrentTodoList.tsx +2 -3
  66. package/src/components/chat/messages/MessageItem.tsx +15 -14
  67. package/src/components/chat/messages/message-utils.ts +1 -1
  68. package/src/components/chat/sender/Sender.scss +8 -3
  69. package/src/components/chat/sender/Sender.tsx +71 -22
  70. package/src/components/chat/session-timeline-panel/git-graph.ts +8 -1
  71. package/src/components/chat/session-timeline-panel/index.scss +2 -2
  72. package/src/components/chat/tools/DefaultTool.tsx +3 -3
  73. package/src/components/chat/tools/adapter-claude/BashTool.tsx +2 -2
  74. package/src/components/chat/tools/adapter-claude/GlobTool.tsx +2 -2
  75. package/src/components/chat/tools/adapter-claude/GrepTool.tsx +2 -2
  76. package/src/components/chat/tools/adapter-claude/LSTool.tsx +4 -4
  77. package/src/components/chat/tools/adapter-claude/ReadTool.scss +1 -2
  78. package/src/components/chat/tools/adapter-claude/ReadTool.tsx +3 -3
  79. package/src/components/chat/tools/adapter-claude/TodoTool.tsx +1 -1
  80. package/src/components/chat/tools/adapter-claude/WriteTool.tsx +2 -2
  81. package/src/components/chat/tools/adapter-claude/components/FileList.scss +4 -2
  82. package/src/components/chat/tools/core/ToolCallBox.scss +34 -35
  83. package/src/components/chat/tools/core/ToolGroup.tsx +5 -5
  84. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +1 -1
  85. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +1 -1
  86. package/src/components/chat/tools/task/StartTasksTool.tsx +2 -2
  87. package/src/components/chat/tools/task/components/TaskRow.tsx +4 -4
  88. package/src/components/chat/tools/task/components/TaskToolCard.tsx +4 -4
  89. package/src/components/config/ConfigAboutSection.tsx +1 -1
  90. package/src/components/config/ConfigSectionForm.tsx +2 -1
  91. package/src/components/config/ConfigSectionPanel.tsx +1 -1
  92. package/src/components/config/ConfigShortcutInput.scss +1 -1
  93. package/src/components/config/ConfigSourceSwitch.tsx +1 -1
  94. package/src/components/config/configSchema.ts +16 -1
  95. package/src/components/config/index.tsx +1 -1
  96. package/src/components/config/record-editors/McpServersRecordEditor.tsx +125 -123
  97. package/src/components/config/record-editors/ModelServicesRecordEditor.tsx +138 -136
  98. package/src/components/config/record-editors/RecordJsonEditor.tsx +31 -29
  99. package/src/components/config/record-editors/index.tsx +1 -1
  100. package/src/components/knowledge-base/components/EmptyState.tsx +1 -1
  101. package/src/components/knowledge-base/components/EntitiesTab.tsx +2 -2
  102. package/src/components/knowledge-base/components/EntityItem.tsx +1 -1
  103. package/src/components/knowledge-base/components/EntityList.tsx +1 -1
  104. package/src/components/knowledge-base/components/FilterBar.tsx +2 -2
  105. package/src/components/knowledge-base/components/FlowsTab.tsx +1 -1
  106. package/src/components/knowledge-base/components/KnowledgeList.tsx +1 -1
  107. package/src/components/knowledge-base/components/MetaList.tsx +1 -1
  108. package/src/components/knowledge-base/components/RuleItem.tsx +1 -1
  109. package/src/components/knowledge-base/components/RuleList.tsx +1 -1
  110. package/src/components/knowledge-base/components/RulesTab.tsx +3 -3
  111. package/src/components/knowledge-base/components/SectionHeader.tsx +1 -1
  112. package/src/components/knowledge-base/components/SkillsTab.tsx +3 -3
  113. package/src/components/knowledge-base/components/SpecItem.tsx +1 -1
  114. package/src/components/knowledge-base/components/SpecList.tsx +1 -1
  115. package/src/components/knowledge-base/components/TabContent.tsx +1 -1
  116. package/src/components/knowledge-base/components/TabLabel.tsx +1 -1
  117. package/src/components/sidebar/SessionItem.scss +0 -1
  118. package/src/components/sidebar/SessionItem.tsx +1 -1
  119. package/src/hooks/chat/model-selector.ts +115 -121
  120. package/src/hooks/chat/use-chat-adapter.ts +3 -3
  121. package/src/hooks/chat/use-chat-interaction.ts +1 -1
  122. package/src/hooks/chat/use-chat-model-adapter-selection.tsx +549 -0
  123. package/src/hooks/chat/use-chat-models.tsx +7 -2
  124. package/src/hooks/chat/use-chat-permission-mode.ts +5 -1
  125. package/src/hooks/chat/use-chat-session-messages.ts +2 -2
  126. package/src/hooks/chat/use-chat-session.ts +14 -12
  127. package/src/hooks/chat/use-chat-view.ts +1 -1
  128. package/src/hooks/use-app-preferences.ts +14 -4
  129. package/src/hooks/use-session-subscription.ts +17 -6
  130. package/src/hooks/useQueryParams.ts +8 -6
  131. package/src/resources/adapters.ts +8 -2
  132. package/src/resources/locales/en.json +14 -1
  133. package/src/resources/locales/zh.json +14 -1
  134. package/src/routes/ChatRoute.scss +5 -1
  135. package/src/runtime-config.ts +17 -13
  136. package/src/utils/shortcutUtils.ts +1 -1
  137. package/vite.config.ts +5 -0
  138. package/dist/assets/channel-Hxo8SEEx.js +0 -1
  139. package/dist/assets/clone-Dd_kUYh5.js +0 -1
  140. package/dist/assets/flowDiagram-v2-4f6560a1-CmztIxNZ.js +0 -1
  141. package/dist/assets/index-Cw-fkktx.js +0 -557
@@ -42,47 +42,49 @@ export const RecordJsonEditor = ({
42
42
  key={key}
43
43
  className={`config-view__record-card${isCollapsed ? ' config-view__record-card--collapsed' : ''}`}
44
44
  >
45
- <div className='config-view__record-title'>
46
- <div className='config-view__record-title-left'>
47
- <Tooltip title={isCollapsed ? t('config.editor.expand') : t('config.editor.collapse')}>
45
+ <div className='config-view__record-title'>
46
+ <div className='config-view__record-title-left'>
47
+ <Tooltip title={isCollapsed ? t('config.editor.expand') : t('config.editor.collapse')}>
48
+ <Button
49
+ size='small'
50
+ type='text'
51
+ className='config-view__icon-button config-view__icon-button--compact'
52
+ aria-label={isCollapsed ? t('config.editor.expand') : t('config.editor.collapse')}
53
+ icon={
54
+ <span className='material-symbols-rounded'>{isCollapsed ? 'chevron_right' : 'expand_more'}</span>
55
+ }
56
+ onClick={() => {
57
+ setCollapsedKeys(prev => ({ ...prev, [key]: !isCollapsed }))
58
+ }}
59
+ />
60
+ </Tooltip>
61
+ <span>{key}</span>
62
+ </div>
63
+ <Tooltip title={t('config.editor.remove')}>
48
64
  <Button
49
65
  size='small'
50
66
  type='text'
67
+ danger
51
68
  className='config-view__icon-button config-view__icon-button--compact'
52
- aria-label={isCollapsed ? t('config.editor.expand') : t('config.editor.collapse')}
53
- icon={<span className='material-symbols-rounded'>{isCollapsed ? 'chevron_right' : 'expand_more'}</span>}
69
+ aria-label={t('config.editor.remove')}
70
+ icon={<span className='material-symbols-rounded'>delete</span>}
54
71
  onClick={() => {
55
- setCollapsedKeys(prev => ({ ...prev, [key]: !isCollapsed }))
72
+ const updated = { ...value }
73
+ delete updated[key]
74
+ onChange(updated)
56
75
  }}
57
76
  />
58
77
  </Tooltip>
59
- <span>{key}</span>
60
78
  </div>
61
- <Tooltip title={t('config.editor.remove')}>
62
- <Button
63
- size='small'
64
- type='text'
65
- danger
66
- className='config-view__icon-button config-view__icon-button--compact'
67
- aria-label={t('config.editor.remove')}
68
- icon={<span className='material-symbols-rounded'>delete</span>}
69
- onClick={() => {
70
- const updated = { ...value }
71
- delete updated[key]
72
- onChange(updated)
79
+ <div className='config-view__record-body'>
80
+ <ComplexTextEditor
81
+ value={itemValue}
82
+ onChange={(next) => {
83
+ onChange({ ...value, [key]: next })
73
84
  }}
74
85
  />
75
- </Tooltip>
76
- </div>
77
- <div className='config-view__record-body'>
78
- <ComplexTextEditor
79
- value={itemValue}
80
- onChange={(next) => {
81
- onChange({ ...value, [key]: next })
82
- }}
83
- />
86
+ </div>
84
87
  </div>
85
- </div>
86
88
  )
87
89
  })}
88
90
  <div className='config-view__record-add'>
@@ -1,5 +1,5 @@
1
- export { ChannelRecordEditor } from './ChannelRecordEditor'
2
1
  export { BooleanRecordEditor } from './BooleanRecordEditor'
2
+ export { ChannelRecordEditor } from './ChannelRecordEditor'
3
3
  export { KeyValueEditor } from './KeyValueEditor'
4
4
  export { McpServersRecordEditor } from './McpServersRecordEditor'
5
5
  export { ModelServicesRecordEditor } from './ModelServicesRecordEditor'
@@ -4,7 +4,7 @@ import { Empty } from 'antd'
4
4
 
5
5
  import { ActionButton } from './ActionButton'
6
6
 
7
- type EmptyStateProps = {
7
+ interface EmptyStateProps {
8
8
  description: string
9
9
  actionLabel?: string
10
10
  onAction?: () => void
@@ -42,7 +42,7 @@ export function EntitiesTab({
42
42
  <SectionHeader
43
43
  title={t('knowledge.entities.title')}
44
44
  description={t('knowledge.entities.desc')}
45
- actions={(
45
+ actions={
46
46
  <Space>
47
47
  <ActionButton
48
48
  icon={<span className='material-symbols-rounded'>download</span>}
@@ -58,7 +58,7 @@ export function EntitiesTab({
58
58
  {t('knowledge.entities.create')}
59
59
  </ActionButton>
60
60
  </Space>
61
- )}
61
+ }
62
62
  />
63
63
  <FilterBar
64
64
  query={query}
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
6
6
  import type { EntitySummary } from '#~/api.js'
7
7
  import { MetaList } from './MetaList'
8
8
 
9
- type EntityItemProps = {
9
+ interface EntityItemProps {
10
10
  entity: EntitySummary
11
11
  }
12
12
 
@@ -9,7 +9,7 @@ import { EntityItem } from './EntityItem'
9
9
  import { KnowledgeList } from './KnowledgeList'
10
10
  import { LoadingState } from './LoadingState'
11
11
 
12
- type EntityListProps = {
12
+ interface EntityListProps {
13
13
  isLoading: boolean
14
14
  entities: EntitySummary[]
15
15
  filteredEntities: EntitySummary[]
@@ -2,12 +2,12 @@ import './FilterBar.scss'
2
2
 
3
3
  import { Input, Select } from 'antd'
4
4
 
5
- type FilterOption = {
5
+ interface FilterOption {
6
6
  label: string
7
7
  value: string
8
8
  }
9
9
 
10
- type FilterBarProps = {
10
+ interface FilterBarProps {
11
11
  query: string
12
12
  tagOptions: FilterOption[]
13
13
  tagFilter: string[]
@@ -10,7 +10,7 @@ import { SectionHeader } from './SectionHeader'
10
10
  import { SpecList } from './SpecList'
11
11
  import { TabContent } from './TabContent'
12
12
 
13
- type FlowsTabProps = {
13
+ interface FlowsTabProps {
14
14
  specs: SpecSummary[]
15
15
  filteredSpecs: SpecSummary[]
16
16
  isLoading: boolean
@@ -3,7 +3,7 @@ import './KnowledgeList.scss'
3
3
  import { List } from 'antd'
4
4
  import type { ReactNode } from 'react'
5
5
 
6
- type KnowledgeListProps<T> = {
6
+ interface KnowledgeListProps<T> {
7
7
  data: T[]
8
8
  renderItem: (item: T) => ReactNode
9
9
  }
@@ -1,6 +1,6 @@
1
1
  import './MetaList.scss'
2
2
 
3
- type MetaListProps = {
3
+ interface MetaListProps {
4
4
  items: string[]
5
5
  }
6
6
 
@@ -8,7 +8,7 @@ import { getRuleDetail } from '#~/api.js'
8
8
  import { MarkdownContent } from '#~/components/MarkdownContent'
9
9
  import { LoadingState } from './LoadingState'
10
10
 
11
- type RuleItemProps = {
11
+ interface RuleItemProps {
12
12
  rule: RuleSummary
13
13
  }
14
14
 
@@ -9,7 +9,7 @@ import { KnowledgeList } from './KnowledgeList'
9
9
  import { LoadingState } from './LoadingState'
10
10
  import { RuleItem } from './RuleItem'
11
11
 
12
- type RuleListProps = {
12
+ interface RuleListProps {
13
13
  isLoading: boolean
14
14
  rules: RuleSummary[]
15
15
  filteredRules: RuleSummary[]
@@ -9,7 +9,7 @@ import { RuleList } from './RuleList'
9
9
  import { SectionHeader } from './SectionHeader'
10
10
  import { TabContent } from './TabContent'
11
11
 
12
- type RulesTabProps = {
12
+ interface RulesTabProps {
13
13
  rules: RuleSummary[]
14
14
  filteredRules: RuleSummary[]
15
15
  isLoading: boolean
@@ -35,7 +35,7 @@ export function RulesTab({
35
35
  <SectionHeader
36
36
  title={t('knowledge.rules.title')}
37
37
  description={t('knowledge.rules.desc')}
38
- actions={(
38
+ actions={
39
39
  <Space>
40
40
  <ActionButton
41
41
  icon={<span className='material-symbols-rounded'>download</span>}
@@ -51,7 +51,7 @@ export function RulesTab({
51
51
  {t('knowledge.rules.create')}
52
52
  </ActionButton>
53
53
  </Space>
54
- )}
54
+ }
55
55
  />
56
56
  <div className='knowledge-base-view__filters'>
57
57
  <Input
@@ -2,7 +2,7 @@ import './SectionHeader.scss'
2
2
 
3
3
  import type { ReactNode } from 'react'
4
4
 
5
- type SectionHeaderProps = {
5
+ interface SectionHeaderProps {
6
6
  title: string
7
7
  description: string
8
8
  actions?: ReactNode
@@ -8,7 +8,7 @@ import { EmptyState } from './EmptyState'
8
8
  import { SectionHeader } from './SectionHeader'
9
9
  import { TabContent } from './TabContent'
10
10
 
11
- type SkillsTabProps = {
11
+ interface SkillsTabProps {
12
12
  onCreate: () => void
13
13
  onImport: () => void
14
14
  }
@@ -21,7 +21,7 @@ export function SkillsTab({ onCreate, onImport }: SkillsTabProps) {
21
21
  <SectionHeader
22
22
  title={t('knowledge.skills.title')}
23
23
  description={t('knowledge.skills.desc')}
24
- actions={(
24
+ actions={
25
25
  <Space>
26
26
  <ActionButton
27
27
  icon={<span className='material-symbols-rounded'>download</span>}
@@ -37,7 +37,7 @@ export function SkillsTab({ onCreate, onImport }: SkillsTabProps) {
37
37
  {t('knowledge.skills.create')}
38
38
  </ActionButton>
39
39
  </Space>
40
- )}
40
+ }
41
41
  />
42
42
  <EmptyState
43
43
  description={t('knowledge.skills.empty')}
@@ -11,7 +11,7 @@ import { MarkdownContent } from '#~/components/MarkdownContent'
11
11
  import { LoadingState } from './LoadingState'
12
12
  import { MetaList } from './MetaList'
13
13
 
14
- type SpecItemProps = {
14
+ interface SpecItemProps {
15
15
  spec: SpecSummary
16
16
  }
17
17
 
@@ -9,7 +9,7 @@ import { KnowledgeList } from './KnowledgeList'
9
9
  import { LoadingState } from './LoadingState'
10
10
  import { SpecItem } from './SpecItem'
11
11
 
12
- type SpecListProps = {
12
+ interface SpecListProps {
13
13
  isLoading: boolean
14
14
  specs: SpecSummary[]
15
15
  filteredSpecs: SpecSummary[]
@@ -2,7 +2,7 @@ import './TabContent.scss'
2
2
 
3
3
  import type { ReactNode } from 'react'
4
4
 
5
- type TabContentProps = {
5
+ interface TabContentProps {
6
6
  children: ReactNode
7
7
  className?: string
8
8
  }
@@ -1,6 +1,6 @@
1
1
  import './TabLabel.scss'
2
2
 
3
- type TabLabelProps = {
3
+ interface TabLabelProps {
4
4
  icon: string
5
5
  label: string
6
6
  }
@@ -221,7 +221,7 @@ export function SessionItem({
221
221
  </Tooltip>
222
222
  </div>
223
223
  <div className='tags-container'>
224
- {session.tags?.map(tag => {
224
+ {session.tags?.map((tag: string) => {
225
225
  const automationTag = parseAutomationTag(tag)
226
226
  if (automationTag) {
227
227
  const href = `/automation?rule=${encodeURIComponent(automationTag.ruleId)}`
@@ -1,150 +1,144 @@
1
- import type { ModelServiceConfig } from '@vibe-forge/core'
2
-
3
- export interface ServiceModelEntry {
4
- serviceKey: string
5
- model: string
6
- selectorValue: string
7
- }
8
-
9
- const normalizeNonEmptyString = (value: string | undefined) => {
10
- const normalized = typeof value === 'string' ? value.trim() : ''
11
- return normalized === '' ? undefined : normalized
1
+ import type { AdapterBuiltinModel, ModelMetadataConfig, ServiceModelEntry } from '@vibe-forge/types'
2
+ import {
3
+ buildServiceModelSelector,
4
+ listServiceModels,
5
+ mergeAdapterConfigs,
6
+ normalizeNonEmptyString,
7
+ parseServiceModelSelector,
8
+ resolveAdapterConfiguredDefaultModel,
9
+ resolveAdapterModelCompatibility,
10
+ resolveDefaultModelSelection,
11
+ resolveModelDefaultAdapter,
12
+ resolveModelSelection,
13
+ resolveServiceModelSelector
14
+ } from '@vibe-forge/utils/model-selection'
15
+
16
+ export type { ServiceModelEntry }
17
+ export {
18
+ buildServiceModelSelector,
19
+ listServiceModels,
20
+ mergeAdapterConfigs,
21
+ normalizeNonEmptyString,
22
+ parseServiceModelSelector,
23
+ resolveAdapterModelCompatibility,
24
+ resolveServiceModelSelector
12
25
  }
13
26
 
14
- export const buildServiceModelSelector = (serviceKey: string, modelName: string) => `${serviceKey},${modelName}`
15
-
16
- export const parseServiceModelSelector = (value: string | undefined) => {
17
- const normalizedValue = normalizeNonEmptyString(value)
18
- if (!normalizedValue || !normalizedValue.includes(',')) return undefined
19
-
20
- const [serviceKey, modelName] = normalizedValue.split(/,(.+)/)
21
- const normalizedServiceKey = normalizeNonEmptyString(serviceKey)
22
- const normalizedModelName = normalizeNonEmptyString(modelName)
23
- if (!normalizedServiceKey || !normalizedModelName) return undefined
24
-
25
- return {
26
- serviceKey: normalizedServiceKey,
27
- modelName: normalizedModelName,
28
- selectorValue: buildServiceModelSelector(normalizedServiceKey, normalizedModelName)
29
- }
27
+ export const resolveChatModelSelection = (params: {
28
+ value?: string
29
+ builtinModels?: Iterable<string>
30
+ serviceModels: ServiceModelEntry[]
31
+ defaultModelService?: string
32
+ preserveUnknown?: boolean
33
+ }) => {
34
+ return resolveModelSelection({
35
+ value: params.value,
36
+ builtinModels: params.builtinModels,
37
+ serviceModels: params.serviceModels,
38
+ preferredServiceKey: params.defaultModelService,
39
+ preserveUnknown: params.preserveUnknown
40
+ })
30
41
  }
31
42
 
32
- export const listServiceModels = (modelServices: Record<string, ModelServiceConfig>) => {
33
- const list: ServiceModelEntry[] = []
34
-
35
- for (const [serviceKey, serviceValue] of Object.entries(modelServices)) {
36
- const normalizedServiceKey = normalizeNonEmptyString(serviceKey)
37
- if (!normalizedServiceKey) continue
38
-
39
- const service = (serviceValue != null && typeof serviceValue === 'object')
40
- ? serviceValue
41
- : undefined
42
- const models = Array.isArray(service?.models) ? service.models : []
43
-
44
- for (const model of models) {
45
- const normalizedModel = normalizeNonEmptyString(model)
46
- if (!normalizedModel) continue
47
-
48
- list.push({
49
- serviceKey: normalizedServiceKey,
50
- model: normalizedModel,
51
- selectorValue: buildServiceModelSelector(normalizedServiceKey, normalizedModel)
52
- })
53
- }
54
- }
55
-
56
- return list
43
+ export const resolveDefaultChatModelSelection = (params: {
44
+ defaultModel?: string
45
+ defaultModelService?: string
46
+ builtinModels?: Iterable<string>
47
+ serviceModels: ServiceModelEntry[]
48
+ preserveUnknownDefaultModel?: boolean
49
+ }) => {
50
+ return resolveDefaultModelSelection({
51
+ defaultModel: params.defaultModel,
52
+ defaultModelService: params.defaultModelService,
53
+ builtinModels: params.builtinModels,
54
+ serviceModels: params.serviceModels,
55
+ preserveUnknownDefaultModel: params.preserveUnknownDefaultModel
56
+ })
57
57
  }
58
58
 
59
- const findExactServiceModel = (serviceModels: ServiceModelEntry[], serviceKey: string, modelName: string) => (
60
- serviceModels.find(entry => entry.serviceKey === serviceKey && entry.model === modelName)
61
- )
62
-
63
- export const resolveServiceModelSelector = (params: {
59
+ export const resolveChatAdapterSelection = (params: {
64
60
  value?: string
65
- serviceModels: ServiceModelEntry[]
66
- preferredServiceKey?: string
61
+ availableAdapters: Iterable<string>
62
+ defaultAdapter?: string
63
+ preserveUnknown?: boolean
67
64
  }) => {
68
65
  const normalizedValue = normalizeNonEmptyString(params.value)
69
- if (!normalizedValue) return undefined
66
+ const adapterKeys = Array.from(params.availableAdapters)
67
+ const adapterSet = new Set(adapterKeys)
68
+ const normalizedDefaultAdapter = normalizeNonEmptyString(params.defaultAdapter)
70
69
 
71
- const parsed = parseServiceModelSelector(normalizedValue)
72
- if (parsed) {
73
- const exactMatch = findExactServiceModel(params.serviceModels, parsed.serviceKey, parsed.modelName)
74
- if (exactMatch) return exactMatch.selectorValue
70
+ if (normalizedValue) {
71
+ if (adapterSet.has(normalizedValue)) return normalizedValue
72
+ if (params.preserveUnknown === true) return normalizedValue
75
73
  }
74
+ if (normalizedDefaultAdapter && adapterSet.has(normalizedDefaultAdapter)) return normalizedDefaultAdapter
75
+ return adapterKeys[0]
76
+ }
76
77
 
77
- const modelName = parsed?.modelName ?? normalizedValue
78
- const candidates = params.serviceModels.filter(entry => entry.model === modelName)
79
- if (candidates.length === 0) return undefined
80
-
81
- const preferredKeys = [parsed?.serviceKey, normalizeNonEmptyString(params.preferredServiceKey)]
82
- .filter((value, index, array): value is string => Boolean(value) && array.indexOf(value) === index)
83
-
84
- for (const preferredKey of preferredKeys) {
85
- const candidate = candidates.find(entry => entry.serviceKey === preferredKey)
86
- if (candidate) return candidate.selectorValue
87
- }
78
+ const findBuiltinModelAdapters = (
79
+ model: string | undefined,
80
+ adapterBuiltinModels: Record<string, AdapterBuiltinModel[]>
81
+ ) => {
82
+ const normalizedModel = normalizeNonEmptyString(model)
83
+ if (!normalizedModel) return []
88
84
 
89
- return candidates[0]?.selectorValue
85
+ return Object.entries(adapterBuiltinModels)
86
+ .filter(([, models]) => Array.isArray(models) && models.some(item => item?.value === normalizedModel))
87
+ .map(([adapterKey]) => adapterKey)
90
88
  }
91
89
 
92
- export const resolveChatModelSelection = (params: {
93
- value?: string
94
- builtinModels?: Iterable<string>
95
- serviceModels: ServiceModelEntry[]
96
- defaultModelService?: string
90
+ export const resolveAdapterForChatModelSelection = (params: {
91
+ model?: string
92
+ availableAdapters: Iterable<string>
93
+ defaultAdapter?: string
94
+ adapterBuiltinModels?: Record<string, AdapterBuiltinModel[]>
95
+ modelMetadata?: Record<string, ModelMetadataConfig>
97
96
  }) => {
98
- const normalizedValue = normalizeNonEmptyString(params.value)
99
- if (!normalizedValue) return undefined
97
+ const adapterKeys = Array.from(params.availableAdapters)
98
+ const adapterSet = new Set(adapterKeys)
99
+ const metadataAdapter = resolveModelDefaultAdapter({
100
+ model: params.model,
101
+ models: params.modelMetadata
102
+ })
103
+ if (metadataAdapter && adapterSet.has(metadataAdapter)) return metadataAdapter
100
104
 
101
- const builtinModelSet = new Set(
102
- Array.from(params.builtinModels ?? [])
103
- .map(item => normalizeNonEmptyString(item))
104
- .filter((item): item is string => Boolean(item))
105
+ const builtinAdapters = findBuiltinModelAdapters(
106
+ params.model,
107
+ params.adapterBuiltinModels ?? {}
105
108
  )
109
+ const normalizedDefaultAdapter = normalizeNonEmptyString(params.defaultAdapter)
110
+ if (normalizedDefaultAdapter && builtinAdapters.includes(normalizedDefaultAdapter)) return normalizedDefaultAdapter
111
+ if (builtinAdapters.length > 0) return builtinAdapters[0]
106
112
 
107
- if (builtinModelSet.has(normalizedValue)) return normalizedValue
108
-
109
- return resolveServiceModelSelector({
110
- value: normalizedValue,
111
- serviceModels: params.serviceModels,
112
- preferredServiceKey: params.defaultModelService
113
+ return resolveChatAdapterSelection({
114
+ availableAdapters: adapterKeys,
115
+ defaultAdapter: params.defaultAdapter
113
116
  })
114
117
  }
115
118
 
116
- export const resolveDefaultChatModelSelection = (params: {
119
+ export const resolveModelForChatAdapterSelection = (params: {
120
+ adapter?: string
121
+ adapters?: Record<string, unknown>
117
122
  defaultModel?: string
118
123
  defaultModelService?: string
119
124
  builtinModels?: Iterable<string>
125
+ fallbackBuiltinModels?: Iterable<string>
120
126
  serviceModels: ServiceModelEntry[]
121
127
  }) => {
122
- const builtinModels = Array.from(params.builtinModels ?? [])
123
- .map(item => normalizeNonEmptyString(item))
124
- .filter((item): item is string => Boolean(item))
125
- const builtinModelSet = new Set(builtinModels)
126
- const normalizedDefaultModel = normalizeNonEmptyString(params.defaultModel)
127
-
128
- if (normalizedDefaultModel) {
129
- const parsed = parseServiceModelSelector(normalizedDefaultModel)
130
- const resolvedServiceModel = resolveServiceModelSelector({
131
- value: normalizedDefaultModel,
132
- serviceModels: params.serviceModels,
133
- preferredServiceKey: parsed?.serviceKey ?? params.defaultModelService
134
- })
135
- if (resolvedServiceModel) return resolvedServiceModel
136
-
137
- if (builtinModelSet.has(normalizedDefaultModel)) return normalizedDefaultModel
138
- if (parsed?.modelName && builtinModelSet.has(parsed.modelName)) return parsed.modelName
139
- }
140
-
141
- const normalizedDefaultModelService = normalizeNonEmptyString(params.defaultModelService)
142
- if (normalizedDefaultModelService) {
143
- const defaultServiceModel = params.serviceModels.find(entry => entry.serviceKey === normalizedDefaultModelService)
144
- if (defaultServiceModel) return defaultServiceModel.selectorValue
145
- }
146
-
147
- if (builtinModels.length > 0) return builtinModels[0]
128
+ const adapterConfiguredModel = resolveAdapterConfiguredDefaultModel({
129
+ adapterConfig: params.adapter != null ? params.adapters?.[params.adapter] : undefined,
130
+ builtinModels: params.builtinModels,
131
+ serviceModels: params.serviceModels,
132
+ preferredServiceKey: params.defaultModelService,
133
+ preserveUnknown: false
134
+ })
135
+ if (adapterConfiguredModel) return adapterConfiguredModel
148
136
 
149
- return params.serviceModels[0]?.selectorValue
137
+ return resolveDefaultChatModelSelection({
138
+ defaultModel: params.defaultModel,
139
+ defaultModelService: params.defaultModelService,
140
+ builtinModels: params.builtinModels ?? params.fallbackBuiltinModels,
141
+ serviceModels: params.serviceModels,
142
+ preserveUnknownDefaultModel: false
143
+ })
150
144
  }
@@ -1,10 +1,10 @@
1
- import { type ReactNode, createElement } from 'react'
2
- import { useEffect, useMemo, useState } from 'react'
1
+ import { createElement, useEffect, useMemo, useState } from 'react'
2
+ import type { ReactNode } from 'react'
3
3
  import useSWR from 'swr'
4
4
 
5
5
  import { getConfig } from '#~/api.js'
6
6
  import { getAdapterDisplay } from '#~/resources/adapters.js'
7
- import type { ConfigResponse } from '@vibe-forge/core'
7
+ import type { ConfigResponse } from '@vibe-forge/types'
8
8
 
9
9
  const ADAPTER_STORAGE_KEY = 'vf_chat_adapter'
10
10
 
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useState } from 'react'
2
2
 
3
- import type { AskUserQuestionParams } from '@vibe-forge/core'
4
3
  import { connectionManager } from '#~/connectionManager.js'
4
+ import type { AskUserQuestionParams } from '@vibe-forge/core'
5
5
 
6
6
  export function useChatInteraction({ sessionId }: { sessionId?: string }) {
7
7
  const [interactionRequest, setInteractionRequest] = useState<{ id: string; payload: AskUserQuestionParams } | null>(