Skip to content

Commit 654ef71

Browse files
Sync public snapshot from freebuff-private
Source: CodebuffAI/freebuff-private@15cb1ec9a6c680d981325868ce8211025697b637
1 parent e19e6d4 commit 654ef71

3 files changed

Lines changed: 71 additions & 6 deletions

File tree

agents/base-chat.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import { publisher } from './constants'
55
import type { SecretAgentDefinition } from './types/secret-agent-definition'
66

77
/**
8-
* Conversational agent behind freebuff.com/chat. Runs with no filesystem and
9-
* no direct tools, but can spawn researcher-web to look things up on the
10-
* live internet. The chat server may override `model` per request (DeepSeek
11-
* Flash vs Pro for full-access users).
8+
* Conversational agent behind freebuff.com/chat. Runs with no filesystem, but
9+
* can spawn researcher-web to look things up on the live internet and call
10+
* gravity_index to recommend third-party developer services. The chat server
11+
* may override `model` per request (DeepSeek Flash vs Pro for full-access
12+
* users).
1213
*/
1314
const definition: SecretAgentDefinition = {
1415
id: 'base-chat',
@@ -23,12 +24,14 @@ const definition: SecretAgentDefinition = {
2324
},
2425
},
2526
outputMode: 'last_message',
26-
toolNames: ['spawn_agents'],
27+
toolNames: ['spawn_agents', 'gravity_index'],
2728
spawnableAgents: ['researcher-web'],
2829

2930
systemPrompt: `You are Freebuff Chat, a friendly, sharp assistant made by Freebuff (freebuff.com), the home of free AI coding tools. You are chatting with a user in a web interface that renders markdown.`,
3031
instructionsPrompt: `Be direct and helpful. Use markdown when it improves clarity (code blocks, lists, tables), and keep answers as short as they can be while fully answering the question.
3132
33+
When the user is choosing a third-party developer service (database, auth, payments, hosting, email, monitoring, analytics, AI APIs, storage, CMS, search, etc.) or asks what provider to use for something, use the gravity_index tool instead of answering from memory: \`search\` with a query that includes their stack and constraints when they want a recommendation, or \`browse\`/\`list_categories\`/\`get_service\` to explore options. Ground your answer in the result. When a search result includes a tracked setup link (\`credential_request.setup_url\` or \`click_url\`), present that exact URL prominently as a markdown link like "Get your {service} API key" — never swap in the vendor homepage for it. Since you can't edit the user's files, share the relevant setup steps and env vars in chat instead of trying to install anything.
34+
3235
You can search the live internet by spawning the researcher-web agent. Spawn it whenever the answer depends on current or recent information (news, prices, releases, versions, schedules, scores, docs), whenever the user asks you to look something up, or whenever you are not confident in your knowledge. Give it a focused question; you can spawn several in parallel for independent questions. After it reports back, answer the user in your own words and cite source URLs when useful. Don't spawn it for questions you can already answer well (general knowledge, coding help, writing, math).
3336
3437
You do not have access to the user's files or a filesystem — if asked to do something that requires those, say so briefly and help with what you can instead.`,

packages/agent-runtime/src/__tests__/gravity-index-tool.test.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,60 @@ describe('gravity_index tool', () => {
148148
)
149149
})
150150

151+
test('tags base-chat traffic with the freebuff_chat surface', async () => {
152+
const spy = spyOn(webApi, 'callGravityIndexAPI').mockResolvedValue({
153+
result: { search_id: 'search-1' },
154+
})
155+
156+
mockAgentStream([
157+
createToolCallChunk('gravity_index', {
158+
action: 'search',
159+
query: 'transactional email for Next.js',
160+
}),
161+
createToolCallChunk('end_turn', {}),
162+
])
163+
164+
const fileContext = {
165+
...mockFileContext,
166+
agentTemplates: {
167+
'base-chat': {
168+
...gravityTestAgent,
169+
id: 'base-chat',
170+
displayName: 'Freebuff Chat',
171+
},
172+
},
173+
}
174+
const sessionState = getInitialSessionState(fileContext)
175+
const agentState = {
176+
...sessionState.mainAgentState,
177+
agentType: 'base-chat',
178+
}
179+
const { agentTemplates } = assembleLocalAgentTemplates({
180+
...agentRuntimeImpl,
181+
fileContext,
182+
})
183+
184+
await runAgentStep({
185+
...runAgentStepBaseParams,
186+
agentType: 'base-chat',
187+
fileContext,
188+
localAgentTemplates: agentTemplates,
189+
agentTemplate: agentTemplates['base-chat'],
190+
agentState,
191+
prompt: 'Find an email provider',
192+
})
193+
194+
expect(spy).toHaveBeenCalledWith(
195+
expect.objectContaining({
196+
input: expect.objectContaining({
197+
metadata: expect.objectContaining({
198+
surface: 'freebuff_chat',
199+
}),
200+
}),
201+
}),
202+
)
203+
})
204+
151205
test('stores recommendation and setup URL in tool output', async () => {
152206
spyOn(webApi, 'callGravityIndexAPI').mockResolvedValue({
153207
result: {

packages/agent-runtime/src/tools/handlers/tool/gravity-index.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
CodebuffToolCall,
88
CodebuffToolOutput,
99
} from '@codebuff/common/tools/list'
10+
import type { AgentTemplate } from '@codebuff/common/types/agent-template'
1011
import type { ClientEnv, CiEnv } from '@codebuff/common/types/contracts/env'
1112
import type { JSONObject, JSONValue } from '@codebuff/common/types/json'
1213
import type { Logger } from '@codebuff/common/types/contracts/logger'
@@ -24,9 +25,15 @@ const omitUndefined = (value: Record<string, JSONValue | undefined>) => {
2425
const isJSONObject = (value: JSONValue | undefined): value is JSONObject =>
2526
!!value && typeof value === 'object' && !Array.isArray(value)
2627

28+
/** Gravity attribution surface, so clicks/conversions are attributable to the
29+
* product the request came from rather than all reading as CLI traffic. */
30+
const gravitySurface = (agentTemplate: { id: string }): string =>
31+
agentTemplate.id === 'base-chat' ? 'freebuff_chat' : 'codebuff_cli'
32+
2733
export const handleGravityIndex = (async (params: {
2834
previousToolCallFinished: Promise<void>
2935
toolCall: CodebuffToolCall<'gravity_index'>
36+
agentTemplate: AgentTemplate
3037
logger: Logger
3138
apiKey: string
3239

@@ -47,6 +54,7 @@ export const handleGravityIndex = (async (params: {
4754
const {
4855
previousToolCallFinished,
4956
toolCall,
57+
agentTemplate,
5058
agentStepId,
5159
apiKey,
5260
clientSessionId,
@@ -84,7 +92,7 @@ export const handleGravityIndex = (async (params: {
8492
const metadata = {
8593
...existingMetadata,
8694
...omitUndefined({
87-
surface: 'codebuff_cli',
95+
surface: gravitySurface(agentTemplate),
8896
tool_call_id: toolCall.toolCallId,
8997
agent_step_id: agentStepId,
9098
fingerprint_id: fingerprintId,

0 commit comments

Comments
 (0)