Skip to content

Commit 8a1e3f9

Browse files
committed
fix(superagent): fix superagent integration tools
1 parent cceb824 commit 8a1e3f9

4 files changed

Lines changed: 129 additions & 3 deletions

File tree

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,31 @@ describe('parseQuestionAnswerMessage', () => {
8989
})
9090

9191
describe('QuestionDisplay', () => {
92+
it('renders multi-select recap answers as separate, spaced rows', () => {
93+
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
94+
const container = document.createElement('div')
95+
document.body.appendChild(container)
96+
const root = createRoot(container)
97+
98+
act(() => {
99+
root.render(
100+
createElement(QuestionDisplay, {
101+
data: [QUESTIONS[2]],
102+
answers: ['EST, PST'],
103+
})
104+
)
105+
})
106+
107+
const answerContainer = container.querySelector('.gap-1')
108+
expect(Array.from(answerContainer?.children ?? []).map((child) => child.textContent)).toEqual([
109+
'EST',
110+
'PST',
111+
])
112+
113+
act(() => root.unmount())
114+
container.remove()
115+
})
116+
92117
it('focuses the free-text input when Something else is clicked', () => {
93118
;(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true
94119
const container = document.createElement('div')

apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/question/question.tsx

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ export function QuestionDisplay({
156156
{data.map((question, i) => (
157157
<div key={i} className='px-2 py-2'>
158158
<p className='text-[var(--text-primary)] text-sm'>{question.prompt}</p>
159-
<p className='mt-1.5 text-[var(--text-muted)] text-sm'>{recapAnswers[i]}</p>
159+
<div className='mt-1.5 flex flex-col gap-1 text-[var(--text-muted)] text-sm'>
160+
{answerPartsForDisplay(question, recapAnswers[i] ?? '').map((answer, answerIndex) => (
161+
<p key={answerIndex}>{answer}</p>
162+
))}
163+
</div>
160164
</div>
161165
))}
162166
</div>
@@ -481,3 +485,28 @@ function answerFor(question: QuestionItem, selected: string[], custom: string):
481485
const parts = custom.trim() ? [...ordered, custom.trim()] : ordered
482486
return parts.join(', ')
483487
}
488+
489+
/** Separates known multi-select labels for the recap without changing the wire answer. */
490+
function answerPartsForDisplay(question: QuestionItem, answer: string): string[] {
491+
if (question.type !== 'multi_select') return [answer]
492+
493+
const parts: string[] = []
494+
let remaining = answer
495+
496+
for (const option of question.options) {
497+
if (remaining === option.label) {
498+
parts.push(option.label)
499+
remaining = ''
500+
break
501+
}
502+
503+
const optionPrefix = `${option.label}, `
504+
if (remaining.startsWith(optionPrefix)) {
505+
parts.push(option.label)
506+
remaining = remaining.slice(optionPrefix.length)
507+
}
508+
}
509+
510+
if (remaining) parts.push(remaining)
511+
return parts.length > 0 ? parts : [answer]
512+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
6+
vi.mock('@/blocks/registry-maps', () => ({
7+
BLOCK_REGISTRY: {
8+
svc: {
9+
type: 'svc',
10+
tools: { access: ['svc_send_v2'] },
11+
},
12+
// Preview successor sharing the released block's tools (the slack/slack_v2
13+
// paradigm) — must not become the owner of the shared tools.
14+
svc_v2: {
15+
type: 'svc_v2',
16+
preview: true,
17+
tools: { access: ['svc_send_v2'] },
18+
},
19+
// Preview block with tools of its own — stays preview-owned and gated.
20+
newsvc: {
21+
type: 'newsvc',
22+
preview: true,
23+
tools: { access: ['newsvc_do_v1'] },
24+
},
25+
},
26+
}))
27+
28+
vi.mock('@/tools/registry', () => ({
29+
tools: {
30+
svc_send_v1: { name: 'Send (legacy)' },
31+
svc_send_v2: { name: 'Send' },
32+
newsvc_do_v1: { name: 'Do' },
33+
},
34+
}))
35+
36+
import {
37+
filterExposedIntegrationTools,
38+
getExposedIntegrationTools,
39+
resetExposedIntegrationToolsCache,
40+
} from '@/lib/copilot/integration-tools'
41+
42+
describe('getExposedIntegrationTools', () => {
43+
beforeEach(() => {
44+
resetExposedIntegrationToolsCache()
45+
})
46+
47+
it('keeps the released block as owner of tools a preview block shares', () => {
48+
const send = getExposedIntegrationTools().find((t) => t.toolId === 'svc_send_v2')
49+
expect(send).toBeDefined()
50+
expect(send?.blockType).toBe('svc')
51+
expect(send?.preview).toBeFalsy()
52+
})
53+
54+
it('exposes shared tools to viewers without the preview reveal, but not preview-only tools', () => {
55+
const visible = filterExposedIntegrationTools(getExposedIntegrationTools(), null)
56+
expect(visible.some((t) => t.toolId === 'svc_send_v2')).toBe(true)
57+
expect(visible.some((t) => t.toolId === 'newsvc_do_v1')).toBe(false)
58+
})
59+
60+
it('exposes only the latest version of each tool', () => {
61+
const exposed = getExposedIntegrationTools()
62+
expect(exposed.some((t) => t.toolId === 'svc_send_v1')).toBe(false)
63+
expect(exposed.some((t) => t.toolId === 'svc_send_v2')).toBe(true)
64+
})
65+
})

apps/sim/lib/copilot/integration-tools.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,15 @@ export function getExposedIntegrationTools(): ExposedIntegrationTool[] {
5353
const service = stripVersionSuffix(block.type)
5454
const owner = { service, blockType: block.type, preview: block.preview }
5555
for (const toolId of block.tools.access) {
56-
toolToBlock.set(toolId, owner)
57-
toolToBlock.set(stripVersionSuffix(toolId), owner)
56+
for (const key of [toolId, stripVersionSuffix(toolId)]) {
57+
// A preview block must not steal ownership of tools it shares with a
58+
// released block (e.g. slack_v2 spreads slack's tools.access), or the
59+
// per-viewer filter would hide those tools from everyone without the
60+
// preview reveal.
61+
const existing = toolToBlock.get(key)
62+
if (existing && !existing.preview && owner.preview) continue
63+
toolToBlock.set(key, owner)
64+
}
5865
}
5966
}
6067

0 commit comments

Comments
 (0)