Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 21 additions & 19 deletions agents/file-explorer/code-searcher.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { publisher } from '../constants'

import type { SecretAgentDefinition } from '../types/secret-agent-definition'
import type { JSONValue } from '../types/util-types'

Expand All @@ -11,28 +10,29 @@ interface SearchQuery {
}

const paramsSchema = {
type: 'object' as const,
type: 'object',
properties: {
searchQueries: {
type: 'array' as const,
type: 'array',
items: {
type: 'object' as const,
type: 'object',
properties: {
pattern: {
type: 'string' as const,
type: 'string',
description: 'The pattern to search for',
},
flags: {
type: 'string' as const,
description: `Optional ripgrep flags to customize the search (e.g., "-i" for case-insensitive, "-g *.ts -g *.js" for TypeScript and JavaScript files only, "-g !*.test.ts" to exclude Typescript test files, "-A 3" for 3 lines after match, "-B 2" for 2 lines before match).`,
type: 'string',
description:
'Optional ripgrep flags to customize the search (e.g., "-i" for case-insensitive, "-g *.ts -g *.js" for TypeScript and JavaScript files only, "-g !*.test.ts" to exclude Typescript test files, "-A 3" for 3 lines after match, "-B 2" for 2 lines before match).',
},
cwd: {
type: 'string' as const,
type: 'string',
description:
'Optional working directory to search within, relative to the project root. Defaults to searching the entire project',
},
maxResults: {
type: 'number' as const,
type: 'number',
description:
'Maximum number of results to return per file. Defaults to 15. There is also a global limit of 250 results across all files',
},
Expand All @@ -43,13 +43,13 @@ const paramsSchema = {
},
},
required: ['searchQueries'],
}
} as const

const codeSearcher: SecretAgentDefinition = {
id: 'code-searcher',
displayName: 'Code Searcher',
spawnerPrompt:
`Mechanically runs multiple code search queries (using ripgrep line-oriented search) and returns up to 250 results across all source files, showing each line that matches the search pattern. Excludes git-ignored files. You MUST pass searchQueries in params. Example input: { "params": { "searchQueries": [{ "pattern": "createUser", "flags": "-g *.ts" }, { "pattern": "deleteUser", "flags": "-g *.ts" }, { "pattern": "UserSchema", "maxResults": 5 }] } }`,
'Mechanically runs multiple code search queries (using ripgrep line-oriented search) and returns up to 250 results across all source files, showing each line that matches the search pattern. Excludes git-ignored files. You MUST pass searchQueries in params. Example input: { "params": { "searchQueries": [{ "pattern": "createUser", "flags": "-g *.ts" }, { "pattern": "deleteUser", "flags": "-g *.ts" }, { "pattern": "UserSchema", "maxResults": 5 }] } }',
model: 'anthropic/claude-sonnet-4.5',
publisher,
includeMessageHistory: false,
Expand All @@ -59,10 +59,11 @@ const codeSearcher: SecretAgentDefinition = {
params: paramsSchema,
},
outputMode: 'structured_output',
handleSteps: function* ({ params }) {
const searchQueries: SearchQuery[] = params?.searchQueries ?? []

*handleSteps({ params }) {
const searchQueries: SearchQuery[] = params?.searchQueries ?? []
const toolResults: JSONValue[] = []

for (const query of searchQueries) {
const { toolResult } = yield {
toolName: 'code_search',
Expand All @@ -73,12 +74,13 @@ const codeSearcher: SecretAgentDefinition = {
maxResults: query.maxResults,
},
}
if (toolResult) {
toolResults.push(
...toolResult
.filter((result) => result.type === 'json')
.map((result) => result.value),
)

if (Array.isArray(toolResult)) {
for (const result of toolResult) {
if (result?.type === 'json' && result.value !== undefined) {
toolResults.push(result.value)
}
}
}
}

Expand Down
7 changes: 2 additions & 5 deletions agents/thinker/thinker-gpt.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
import thinker from './thinker'

import type { SecretAgentDefinition } from '../types/secret-agent-definition'

const definition: SecretAgentDefinition = {
...thinker,
id: 'thinker-gpt',
model: 'openai/gpt-5.4',
providerOptions: undefined,
outputSchema: undefined,
outputMode: 'last_message',
inheritParentSystemPrompt: false,
instructionsPrompt: `You are the thinker-gpt agent. Think deeply about the user request and when satisfied, write out your response.

The parent agent will see your response. DO NOT call any tools. No need to spawn the thinker agent, because you are already the thinker agent. Just do the thinking work now.`,
handleSteps: function* () {
*handleSteps() {
yield 'STEP_ALL'
},
}
Expand Down
24 changes: 12 additions & 12 deletions agents/thinker/thinker.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { OPUS_MODEL, publisher } from '../constants'

import type { SecretAgentDefinition } from '../types/secret-agent-definition'

const definition: SecretAgentDefinition = {
Expand Down Expand Up @@ -40,37 +39,38 @@ You are a thinker agent. Use the <think> tag to think deeply about the user requ
When satisfied, write out a brief response to the user's request. The parent agent will see your response -- no need to call any tools. DO NOT call the set_output tool, as that will be done for you.
`.trim(),

handleSteps: function* () {
*handleSteps() {
const { agentState } = yield 'STEP'

// Find the last assistant message
const lastAssistantMessage = [...agentState.messageHistory]
.reverse()
.find((m) => m.role === 'assistant')
// Find the last assistant message without copying the array using findLast
const lastAssistantMessage = agentState.messageHistory.findLast(
(m) => m.role === 'assistant'
)

if (!lastAssistantMessage) {
const errorMsg =
'Error: No assistant message found in conversation history'
yield {
toolName: 'set_output',
input: { message: errorMsg },
input: {
message: 'Error: No assistant message found in conversation history',
},
}
return
}

// Extract text content from the assistant message
// Extract text content safely from string or structured content array
const content = lastAssistantMessage.content
let textContent = ''

if (typeof content === 'string') {
textContent = content
} else if (Array.isArray(content)) {
textContent = content
.filter((part) => part.type === 'text')
.filter((part): part is { type: 'text'; text: string } => part.type === 'text')
.map((part) => part.text)
.join('')
}

// Remove text within <think> tags (including the tags themselves)
// Strip <think>...</think> tags and sanitize output
const cleanedText = textContent
.replace(/<think>[\s\S]*?<\/think>/g, '')
.trim()
Expand Down