Skip to content

fix: [Agent] Clicking the "Go to Chat" icon on an agent card yields no response, and it is impossible to enter the chat page. - #7859

Merged
shaohuzhang1 merged 1 commit into
v3from
pr@v3@fix_to_chat
Nov 12, 2026
Merged

shaohuzhang1 merged 1 commit into
v3from
pr@v3@fix_to_chat

Conversation

@shaohuzhang1

Copy link
Copy Markdown
Contributor

fix: [Agent] Clicking the "Go to Chat" icon on an agent card yields no response, and it is impossible to enter the chat page.

…o response, and it is impossible to enter the chat page.
<el-button text @click="toChat">
<MkIcon name="icon_new-chat_outlined" />
</el-button>
</MkTooltip>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review

🔴 Bugs / will break at runtime

  1. Unsafe optional chain → TypeError
properties?.api_input_field_list.map(...)

The ?. chain guards up to api_input_field_list, but .map is called directly on it. If api_input_field_list is undefined (very common), this throws inside a computed and breaks rendering of the whole card list.

const list = props.application?.work_flow?.nodes?.find(n => n.id === 'base-node')?.properties?.api_input_field_list ?? []
  1. Empty URL opens a blank tab
const accessUrl = ok.access_token ? `...` : ''
window.open(accessUrl, '_blank')

When the token is missing you call window.open(''), which opens an empty tab. Guard before opening (and ideally show an error/toast).

  1. el-button click bubbles to the card click handler
    The card root has @click="handleOpen", so clicking the chat button also fires the card click (emitting click, potentially navigating). Use @click.stop="toChat".

  2. Malformed URL when there is no query

`${...}/${ok.access_token}/${apiQuery.value}`

With apiQuery === '' you get a trailing slash .../token/; with a query you get .../token/?a=b. The slash before an optional query is almost certainly unintended:

`${window.location.origin}${chatBasePath}/${ok.access_token}${apiQuery.value}`

🟠 Security / robustness

  1. Reverse tabnabbing — always pass noopener,noreferrer:
window.open(url, '_blank', 'noopener,noreferrer')
  1. No error handling / no loading state — .then without .catch gives an unhandled rejection; rapid clicks fire multiple token requests. Prefer async/await + try/catch + a loading ref that disables the button.

  2. No URL encoding — f.variable / f.default_value can contain &, =, spaces, Chinese text, etc.:

return `${encodeURIComponent(f.variable)}=${encodeURIComponent(f.default_value ?? '')}`
  1. Access token in the URL path is exposed in browser history, server logs, and Referer headers. At minimum this is worth a comment/confirmation with the backend; a short-lived token or header-based auth is preferable.

🟡 Code quality / style

  1. Imports are not at the top of the file. import applicationAPI, useRoute/useRouter, computed, and CHAT_API_BASE_PATH are declared after defineOptions/defineProps/defineEmits. This violates import/first, hurts readability and may break some tooling. Consolidate all imports at the top and merge duplicates (vue, vue-router).

  2. Unused import — useRoute is never used.

  3. (f: any) — give it a proper type (ApiInputField), and type the computed's return as string.

  4. Simplification:

const apiQuery = computed(() => {
  const fields = props.application?.work_flow?.nodes
    ?.find(n => n.id === 'base-node')?.properties?.api_input_field_list ?? []
  const query = fields.map(...).join('&')
  return query ? `?${query}` : ''
})
  1. chatBasePath doesn't depend on reactive state — move it to module scope so it isn't recomputed per instance. Also CHAT_API_BASE_PATH.replace(/\/api$/, '') silently does nothing if the constant has a trailing slash (/api/); consider normalizing or asserting.

  2. Business logic leaking into a presentational card. ApplicationCard is now coupled to work_flow internals and the application API. Extract into a composable, e.g. useApplicationChat(application) returning { apiQuery, openChat, loading }.

  3. Double guard mismatch — :class="{ 'cursor-pointer': !selectable }" implies the card is clickable when not selectable, but handleOpen only emits when !selectable. Correct, but the toChat button is rendered unconditionally in the footer; consider hiding/disabling it when !application.is_publish (the sibling v-if block implies unpublished apps shouldn't be chatted with).

  4. Diff noise — the re-formatting of MkResourceCard and MkStatusLabel (re-wrapping attributes) is unrelated to the feature and makes review harder. Revert it or split into a separate commit.

Suggested rewrite (core part)

import { computed, ref } from 'vue'
import { useRouter } from 'vue-router'
import applicationAPI from '@/api/admin/workspace/application/application'
import { CHAT_API_BASE_PATH } from '@/api/constants'

const CHAT_BASE_PATH = CHAT_API_BASE_PATH.replace(/\/api\/?$/, '')
const router = useRouter()

const apiQuery = computed(() => {
  const fields =
    props.application?.work_flow?.nodes
      ?.find(n => n.id === 'base-node')
      ?.properties?.api_input_field_list ?? []

  const query = fields
    .map(f => `${encodeURIComponent(f.variable)}=${encodeURIComponent(f.default_value ?? '')}`)
    .join('&')

  return query ? `?${query}` : ''
})

const chatLoading = ref(false)
async function toChat() {
  if (chatLoading.value) return
  chatLoading.value = true
  try {
    const { access_token } = await applicationAPI.getAccessToken(props.application.id)
    if (!access_token) {
      // ElMessage.error('获取访问令牌失败')
      return
    }
    const url = `${window.location.origin}${CHAT_BASE_PATH}/${access_token}${apiQuery.value}`
    window.open(url, '_blank', 'noopener,noreferrer')
  } catch (e) {
    // ElMessage.error('打开对话失败')
  } finally {
    chatLoading.value = false
  }
}
<el-button text :loading="chatLoading" @click.stop="toChat">

@shaohuzhang1
shaohuzhang1 merged commit b108465 into v3 Nov 12, 2026
6 of 8 checks passed
@shaohuzhang1
shaohuzhang1 deleted the pr@v3@fix_to_chat branch November 12, 2026 06:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant