Skip to content

Commit 88f58ec

Browse files
committed
Merge the chat and search lane
2 parents b919c53 + 73fd7c3 commit 88f58ec

142 files changed

Lines changed: 8835 additions & 3435 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1 @@
1-
import { isServer, type QueryClient } from '@tanstack/react-query'
2-
import { makeQueryClient } from '@/hooks/queries/utils/query-client'
3-
4-
let browserQueryClient: QueryClient | undefined
5-
6-
/**
7-
* Returns a QueryClient instance. On the server, creates a new instance per request.
8-
* On the client, reuses a singleton instance.
9-
*/
10-
export function getQueryClient() {
11-
if (isServer) {
12-
return makeQueryClient()
13-
}
14-
if (!browserQueryClient) {
15-
browserQueryClient = makeQueryClient()
16-
}
17-
return browserQueryClient
18-
}
1+
export { getQueryClient } from '@/hooks/queries/utils/get-query-client'

‎apps/sim/app/workspace/[workspaceId]/home/components/knowledge-search-results/utils.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { connectorDisplayName } from '@/lib/sim-search/connectors'
1010
import {
1111
isHttpUrl,
1212
type SourceTagData,
13-
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags'
13+
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/special-tags/parse'
1414

1515
/**
1616
* One card per document, keeping the best-ranked chunk of each: the list is

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-content.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type {
22
AgentGroupItem,
33
NestedAgentGroup,
44
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-view'
5-
import { isToolDone } from '@/app/workspace/[workspaceId]/home/components/message-content/utils'
5+
import { isToolDone } from '@/app/workspace/[workspaceId]/home/components/message-content/tool-status'
66
import type { ToolCallData } from '@/app/workspace/[workspaceId]/home/types'
77

88
/**

‎apps/sim/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-group.tsx‎

Lines changed: 21 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -3,206 +3,32 @@
33
import { type ComponentType, Fragment, useState } from 'react'
44
import { ActivityStatus } from '@/components/ui/activity-status'
55
import type { ToolActivity } from '@/lib/mothership/generated/protocol'
6-
import {
7-
CallIntegrationTool,
8-
RunCode,
9-
SearchWorkspace,
10-
} from '@/lib/mothership/generated/tool-catalog-v1'
11-
import { getToolActivitySummaryActions, readCallActivity } from '@/lib/mothership/tools/activity'
12-
import { getToolStatusDisplayTitle } from '@/lib/mothership/tools/display'
13-
import { extractStreamingStringArgument } from '@/lib/mothership/tools/streaming-args'
6+
import { readCallActivity } from '@/lib/mothership/tools/activity'
147
import { ActivityStream } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/activity-stream'
15-
import { getNewestRunningTool } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/agent-group-content'
168
import { getSearchActivitySources } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/search-activity-details'
179
import { SearchActivityResults } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/search-activity-results'
18-
import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item'
1910
import {
20-
getActivityAttentionKey,
21-
needsToolInput,
22-
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-interactions'
11+
getActivityHeaderTool,
12+
getActivityStatusTool,
13+
getCompletedActivityLabel,
14+
getInProgressActivityLabel,
15+
getToolTitle,
16+
isAwaitingTitle,
17+
withoutRetriedSearchFailures,
18+
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-labels'
19+
import type { ToolCallItemProps } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-call-item'
20+
import { getActivityAttentionKey } from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-interactions'
2321
import { isToolDone } from '@/app/workspace/[workspaceId]/home/components/message-content/utils'
24-
import { type ToolCallData, ToolCallStatus } from '@/app/workspace/[workspaceId]/home/types'
25-
26-
/** A completed summary names at most this many distinct actions, and never counts the rest. */
27-
const MAX_SUMMARY_ACTIONS = 3
28-
29-
function isFailedTool(tool: ToolCallData): boolean {
30-
return tool.status === ToolCallStatus.error || tool.status === ToolCallStatus.rejected
31-
}
32-
33-
/**
34-
* Drops a search that errored when a later search follows it: the model corrected the query
35-
* and searched again, so the failed attempt is not part of what the user reads. The last search
36-
* always stays, so a failure that no search retried, or a run whose searches all failed, still
37-
* shows its outcome.
38-
*/
39-
function withoutRetriedSearchFailures(tools: ToolCallData[]): ToolCallData[] {
40-
let lastSearch = -1
41-
for (const [index, tool] of tools.entries()) {
42-
if (tool.toolName === SearchWorkspace.id) lastSearch = index
43-
}
44-
return tools.filter(
45-
(tool, index) =>
46-
index >= lastSearch ||
47-
tool.toolName !== SearchWorkspace.id ||
48-
tool.status !== ToolCallStatus.error
49-
)
50-
}
51-
52-
function toolCountLabel(tools: ToolCallData[]): string {
53-
return `${tools.length} tool ${tools.length === 1 ? 'call' : 'calls'}`
54-
}
55-
56-
function getToolTitle(tool: ToolCallData): string {
57-
return getToolStatusDisplayTitle(
58-
tool.displayTitle,
59-
tool.status,
60-
tool.toolName,
61-
tool.activityDescription
62-
)
63-
}
64-
65-
/**
66-
* Summarize completed actions without describing failed or skipped work as
67-
* successful: a single call, or the only successful one, keeps its own title;
68-
* several successful calls name their distinct actions ("Navigated, read
69-
* pages, clicked elements"); stopped or skipped calls append an outcome count.
70-
*/
71-
function getToolActivitySummary(tools: ToolCallData[]): string {
72-
if (tools.length === 1) return getToolTitle(tools[0])
73-
const succeeded = tools.filter((tool) => tool.status === ToolCallStatus.success)
74-
const [first, ...rest] =
75-
succeeded.length === 1
76-
? [getToolTitle(succeeded[0])]
77-
: getToolActivitySummaryActions(succeeded, MAX_SUMMARY_ACTIONS)
78-
const summary = first
79-
? [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(', ')
80-
: toolCountLabel(tools)
81-
return [summary, ...getToolActivityInterruptions(tools)].join(' · ')
82-
}
83-
84-
function getToolActivityInterruptions(tools: ToolCallData[]): string[] {
85-
let stopped = 0
86-
let skipped = 0
87-
for (const tool of tools) {
88-
if (tool.status === ToolCallStatus.cancelled || tool.status === ToolCallStatus.interrupted)
89-
stopped++
90-
else if (tool.status === ToolCallStatus.skipped) skipped++
91-
}
92-
return [...(stopped ? [`${stopped} stopped`] : []), ...(skipped ? [`${skipped} skipped`] : [])]
93-
}
94-
95-
/**
96-
* An executing call whose streamed arguments do not name its action yet, so its
97-
* title is only its tool's placeholder: a search before its arguments arrive,
98-
* a CLI call before its final frame (the verb parsed mid-stream would only flash
99-
* ahead of the model's description), a `run_code` call before its arguments
100-
* resolve, while their parameters hold at most the activity, and an
101-
* integration gateway call before its description streams ("Calling integration").
102-
* A call awaiting approval is never untitled, so its permission card keeps the
103-
* header, and neither is one the model already described.
104-
*/
105-
export function isAwaitingTitle(tool: ToolCallData): boolean {
106-
if (tool.status !== ToolCallStatus.executing || tool.activityDescription) return false
107-
if (tool.toolName === CallIntegrationTool.id) {
108-
const description =
109-
tool.params?.description ?? extractStreamingStringArgument(tool.streamingArgs, 'description')
110-
return !(typeof description === 'string' && description.trim())
111-
}
112-
return (
113-
(tool.toolName === 'sim_cli' ||
114-
tool.toolName.startsWith('cli_') ||
115-
tool.toolName === RunCode.id ||
116-
tool.toolName === 'search_workspace') &&
117-
Object.keys(tool.params ?? {}).every((key) => key === 'activity')
118-
)
119-
}
120-
121-
/**
122-
* The call an in-progress header describes, label and icon alike, shared by
123-
* tool group and subagent headers so a live header never flips to placeholder
124-
* text between steps: the status call, unless it is still awaiting its title,
125-
* in which case the call the header described before it keeps the header
126-
* until the new title arrives. A call waiting on the user is never held, since
127-
* its permission card or handoff would replace the header, and neither is a
128-
* failed call, which must not read as live. With no such earlier call, the
129-
* status call.
130-
*/
131-
export function getActivityHeaderTool(
132-
tools: ToolCallData[],
133-
statusTool: ToolCallData
134-
): ToolCallData {
135-
if (!isAwaitingTitle(statusTool)) return statusTool
136-
return (
137-
getActivityStatusTool(
138-
tools
139-
.slice(
140-
0,
141-
tools.findIndex((tool) => tool.id === statusTool.id)
142-
)
143-
.filter((tool) => !isAwaitingTitle(tool) && !needsToolInput(tool) && !isFailedTool(tool))
144-
) ?? statusTool
145-
)
146-
}
147-
148-
/** The wait wording: the turn between steps, and a first call whose own title is still streaming. */
149-
export const THINKING_LABEL = 'Thinking'
150-
151-
/**
152-
* The title of an activity in progress for its header call, with earlier
153-
* stops and skips kept visible. A header call still awaiting its title has no
154-
* earlier call to hold, so it reads as the activity's intent when the model
155-
* gave one, else as the wait it continues: a tool's placeholder ("Running CLI
156-
* command") would flash between the turn's "Thinking" and the model's description.
157-
*/
158-
export function getInProgressActivityLabel(
159-
activeLabel: string,
160-
headerTool: ToolCallData,
161-
tools: ToolCallData[],
162-
activityTitle: string | undefined
163-
): string {
164-
const label = isAwaitingTitle(headerTool) ? activityTitle || THINKING_LABEL : activeLabel
165-
return getActiveToolActivityTitle(label, headerTool, tools)
166-
}
167-
168-
/** Keep earlier interruptions visible while the latest action continues. */
169-
function getActiveToolActivityTitle(
170-
label: string,
171-
tool: ToolCallData,
172-
tools: ToolCallData[]
173-
): string {
174-
return tool.status === ToolCallStatus.executing || tool.status === ToolCallStatus.success
175-
? [label, ...getToolActivityInterruptions(tools)].join(' · ')
176-
: label
177-
}
178-
179-
/**
180-
* The call a header describes: the newest running call, else the latest call
181-
* that did not error or get rejected, so a failed attempt labels the activity
182-
* only when every call failed. A stopped, skipped, or interrupted call can still be it.
183-
*/
184-
export function getActivityStatusTool(tools: ToolCallData[]): ToolCallData | undefined {
185-
return (
186-
getNewestRunningTool(tools) ??
187-
tools.filter((tool) => !isFailedTool(tool)).at(-1) ??
188-
tools.at(-1)
189-
)
190-
}
191-
192-
/**
193-
* The label of a finished activity: the model's completed title when every
194-
* call succeeded, else the summary of what did succeed, so a stopped, skipped,
195-
* or failed call never hides behind a success title. Main and subagent lanes
196-
* share this rule.
197-
*/
198-
export function getCompletedActivityLabel(
199-
tools: ToolCallData[],
200-
activity: ToolActivity | undefined
201-
): string {
202-
return activity?.completedTitle && tools.every((tool) => tool.status === ToolCallStatus.success)
203-
? activity.completedTitle
204-
: getToolActivitySummary(tools)
205-
}
22+
import type { ToolCallData } from '@/app/workspace/[workspaceId]/home/types'
23+
24+
export {
25+
getActivityHeaderTool,
26+
getActivityStatusTool,
27+
getCompletedActivityLabel,
28+
getInProgressActivityLabel,
29+
isAwaitingTitle,
30+
THINKING_LABEL,
31+
} from '@/app/workspace/[workspaceId]/home/components/message-content/components/agent-group/tool-activity-labels'
20632

20733
interface ToolActivityGroupProps {
20834
activity?: ToolActivity

0 commit comments

Comments
 (0)